react-jalali-calendar
Persian (Jalali / Shamsi) calendar components and date utilities for React.
Built on jalaali-js with no UI framework dependency.
Table of contents
- Installation
- Quick start
- Components
- Headless hook
- Core utilities
- Theming
- TypeScript types
- Week convention
Installation
npm install react-jalali-calendar
# or
pnpm add react-jalali-calendar
# or
yarn add react-jalali-calendarPeer dependencies (must already be in your project):
npm install react react-dom # 18 or 19Quick start
import { useState } from "react";
import { JalaliCalendar, JalaliDatePicker } from "react-jalali-calendar";
import "react-jalali-calendar/styles.css";
export function BookingForm() {
const [date, setDate] = useState(""); // "YYYY-MM-DD" string
const [selected, setSelected] = useState<Date | null>(null);
return (
<div>
{/* Inline calendar */}
<JalaliCalendar
selectedDate={selected}
onSelectDate={setSelected}
latinDigits={false}
/>
{/* Popup date picker */}
<JalaliDatePicker value={date} onChange={setDate} />
</div>
);
}Import
styles.cssonce in your app entry point (e.g.main.tsxorlayout.tsx).
Components
JalaliCalendar
An inline month grid. The component manages its own navigation state internally; you only control which day is selected.
import { JalaliCalendar } from "react-jalali-calendar";
<JalaliCalendar
selectedDate={selected}
onSelectDate={(date) => setSelected(date)}
/>Props
| Prop | Type | Default | Description |
|---|---|---|---|
selectedDate |
Date | null |
required | The currently selected date. Pass null for no selection. |
onSelectDate |
(date: Date) => void |
required | Called with the Date object when the user clicks a day. |
isDateDisabled |
(date: Date) => boolean |
— | Return true to make a day unclickable. |
latinDigits |
boolean |
false |
Show digits as 0-9 instead of ۰-۹. |
initialMonth |
{ jy: number; jm: number } |
current month | Jalali year/month shown on first render. |
prevMonthLabel |
string |
"ماه قبل" |
aria-label for the previous-month button. |
nextMonthLabel |
string |
"ماه بعد" |
aria-label for the next-month button. |
className |
string |
— | Extra CSS class added to the root <div>. |
Examples
// Disable all days in the past
<JalaliCalendar
selectedDate={selected}
onSelectDate={setSelected}
isDateDisabled={(date) => date < new Date()}
/>
// Open on a specific month (Farvardin 1403)
<JalaliCalendar
selectedDate={null}
onSelectDate={setSelected}
initialMonth={{ jy: 1403, jm: 1 }}
/>
// Latin digits + custom class
<JalaliCalendar
selectedDate={selected}
onSelectDate={setSelected}
latinDigits
className="my-calendar"
/>JalaliDatePicker
A button that opens a popover containing JalaliCalendar. Closes on outside click or Escape. The selected date is stored as a "YYYY-MM-DD" string (Gregorian local date key), which is easy to send to a server or store in a form.
import { JalaliDatePicker } from "react-jalali-calendar";
const [date, setDate] = useState(""); // e.g. "2024-03-20"
<JalaliDatePicker value={date} onChange={setDate} />Props
| Prop | Type | Default | Description |
|---|---|---|---|
value |
string |
required | A YYYY-MM-DD Gregorian date key, or "" for no selection. |
onChange |
(value: string) => void |
required | Called with the new YYYY-MM-DD string when a day is picked. |
placeholder |
string |
"انتخاب تاریخ" |
Text shown on the trigger button when no date is selected. |
latinDigits |
boolean |
false |
Display the selected date label with Latin digits. |
isDateDisabled |
(date: Date) => boolean |
— | Passed through to the inner JalaliCalendar. |
className |
string |
— | Extra class on the picker root element. |
calendarClassName |
string |
— | Extra class on the inner JalaliCalendar. |
Examples
// Disable weekends (Friday = index 5 in JS Date)
<JalaliDatePicker
value={date}
onChange={setDate}
isDateDisabled={(d) => d.getDay() === 5}
latinDigits
/>
// With a custom placeholder
<JalaliDatePicker
value={date}
onChange={setDate}
placeholder="تاریخ تولد را انتخاب کنید"
/>Headless hook
Use useJalaliCalendar to build a completely custom calendar UI while letting the library handle month navigation and grid generation.
import { useJalaliCalendar } from "react-jalali-calendar";
import { PERSIAN_MONTHS, PERSIAN_WEEKDAYS_SHORT, localDateKey } from "react-jalali-calendar/core";
function MyCalendar({ selectedDate, onSelectDate }) {
const { jy, jm, grid, today, todayKey, prevMonth, nextMonth, goToMonth, goToToday } =
useJalaliCalendar({ initialMonth: { jy: 1403, jm: 6 } });
return (
<div dir="rtl">
<div>
<button onClick={prevMonth}>قبل</button>
<strong>{PERSIAN_MONTHS[jm - 1]} {jy}</strong>
<button onClick={nextMonth}>بعد</button>
<button onClick={goToToday}>امروز</button>
</div>
<div style={{ display: "grid", gridTemplateColumns: "repeat(7, 1fr)" }}>
{PERSIAN_WEEKDAYS_SHORT.map((d) => (
<span key={d}>{d}</span>
))}
{grid.map((date, i) => {
if (!date) return <span key={i} />;
const key = localDateKey(date);
return (
<button
key={key}
onClick={() => onSelectDate(date)}
style={{ fontWeight: key === todayKey ? "bold" : undefined }}
>
{date.getDate()}
</button>
);
})}
</div>
</div>
);
}Return value
| Property | Type | Description |
|---|---|---|
jy |
number |
Currently displayed Jalali year. |
jm |
number |
Currently displayed Jalali month (1-12). |
grid |
(Date | null)[] |
Flat array of day cells. null entries are leading empty cells before the 1st of the month. |
today |
JalaliDateParts |
Today's { jy, jm, jd }. |
todayKey |
string |
Today as "YYYY-MM-DD", useful for highlighting. |
prevMonth |
() => void |
Navigate one month back. |
nextMonth |
() => void |
Navigate one month forward. |
goToMonth |
(jy, jm) => void |
Jump to any Jalali year/month directly. |
goToToday |
() => void |
Jump back to the current month. |
Options
useJalaliCalendar({
initialMonth: { jy: 1403, jm: 1 }, // optional, defaults to current month
})Core utilities
Import pure date helpers from the /core sub-path. This entry point has no React dependency and can be used in Node.js, server components, or non-React projects.
import {
toJalali,
fromJalali,
formatJalali,
formatTime,
getJalaliMonthGrid,
getJalaliWeekDates,
addJalaliMonths,
monthDateRange,
localDateKey,
isoDateOnly,
toLatinDigits,
toPersianDigits,
PERSIAN_MONTHS,
PERSIAN_WEEKDAYS,
PERSIAN_WEEKDAYS_SHORT,
} from "react-jalali-calendar/core";Conversion
toJalali(date: Date): JalaliDateParts
Convert a Date object to Jalali parts.
toJalali(new Date("2024-03-20"));
// => { jy: 1403, jm: 1, jd: 1 }fromJalali(parts: JalaliDateParts): Date
Convert Jalali { jy, jm, jd } back to a Gregorian Date.
fromJalali({ jy: 1403, jm: 1, jd: 1 });
// => Date("2024-03-20")Formatting
formatJalali(date, options?): string
Human-readable Jalali date string.
formatJalali(new Date("2024-03-20"));
// => "۱ فروردین ۱۴۰۳"
formatJalali(new Date("2024-03-20"), { includeWeekday: true });
// => "چهارشنبه، ۱ فروردین ۱۴۰۳"
formatJalali(new Date("2024-03-20"), { latinDigits: true });
// => "1 فروردین 1403"| Option | Type | Default | Description |
|---|---|---|---|
includeWeekday |
boolean |
false |
Prepend the Persian weekday name. |
latinDigits |
boolean |
false |
Use 0-9 instead of ۰-۹. |
formatTime(date: Date, latinDigits?: boolean): string
Format hours and minutes as HH:MM.
formatTime(new Date(), true); // "09:45"
formatTime(new Date(), false); // "۰۹:۴۵"toPersianDigits(value: string): string
Replace 0-9 with ۰-۹ anywhere in a string.
toPersianDigits("1403/01/01"); // "۱۴۰۳/۰۱/۰۱"toLatinDigits(value: string): string
Replace ۰-۹ with 0-9 anywhere in a string.
toLatinDigits("۱۴۰۳"); // "1403"Date keys
localDateKey(date: Date): string
Returns a "YYYY-MM-DD" string using local time (no UTC shift). This is the format used by JalaliDatePicker.
localDateKey(new Date("2024-03-20")); // "2024-03-20"isoDateOnly(date: Date): string
Returns a "YYYY-MM-DD" string using UTC (date.toISOString().slice(0, 10)).
Grid helpers
getJalaliMonthGrid(jy: number, jm: number): (Date | null)[]
Returns a flat array of cells for a full month grid (Saturday-anchored). Leading cells before the 1st are null.
const cells = getJalaliMonthGrid(1403, 1);
// cells[0] might be null (padding), then Date objects for each daygetJalaliWeekDates(jy: number, jm: number, jd: number): Date[]
Returns the 7 Date objects for the Jalali week (Saturday to Friday) containing the given date.
getJalaliWeekDates(1403, 1, 10); // [Date(Sat), ..., Date(Fri)]addJalaliMonths(jy: number, jm: number, delta: number): JalaliDateParts
Add or subtract months, handling year rollovers correctly.
addJalaliMonths(1403, 12, 1); // => { jy: 1404, jm: 1, jd: 1 }
addJalaliMonths(1403, 1, -1); // => { jy: 1402, jm: 12, jd: 1 }monthDateRange(jy: number, jm: number): { from: Date; to: Date }
Returns the first and last Date of a Jalali month (last date is set to 23:59:59.999). Useful for API range queries.
const { from, to } = monthDateRange(1403, 1);
// from: 2024-03-20 00:00:00
// to: 2024-04-19 23:59:59.999Constants
PERSIAN_MONTHS[0] // "فروردین" (index 0 = month 1)
PERSIAN_WEEKDAYS[0] // "شنبه" (index 0 = Saturday)
PERSIAN_WEEKDAYS_SHORT[0] // "ش"Theming
The components use CSS custom properties scoped under the .rjc and .rjc-picker class names. Override any variable to match your design system.
/* globals.css or any stylesheet loaded after styles.css */
:root {
--rjc-primary: #7c3aed;
--rjc-primary-fg: #ffffff;
--rjc-radius: 0.75rem;
--rjc-font: "Vazirmatn", sans-serif;
}Or scope overrides to a specific instance:
.booking-calendar {
--rjc-primary: #0ea5e9;
--rjc-cell-size: 2.5rem;
}<JalaliCalendar className="booking-calendar" ... />All available variables
| Variable | Controls |
|---|---|
--rjc-bg |
Calendar background |
--rjc-fg |
Text color |
--rjc-muted |
Weekday header text |
--rjc-border |
Border color |
--rjc-primary |
Selected day background |
--rjc-primary-fg |
Selected day text |
--rjc-hover |
Day hover background |
--rjc-today-ring |
Ring color on today's cell |
--rjc-disabled |
Disabled day text color |
--rjc-radius |
Border radius of cells and container |
--rjc-cell-size |
Width/height of each day cell |
--rjc-cell-gap |
Gap between cells in the grid |
--rjc-font |
Font family |
TypeScript types
All public types are exported from the main entry point:
import type {
JalaliDateParts,
JalaliCalendarProps,
JalaliDatePickerProps,
UseJalaliCalendarOptions,
} from "react-jalali-calendar";JalaliDateParts
type JalaliDateParts = {
jy: number; // Jalali year
jm: number; // Jalali month (1-12)
jd: number; // Jalali day (1-29/30/31)
};Week convention
This library treats Saturday as day 0 and Friday as day 6, which matches the Iranian week. The grid returned by getJalaliMonthGrid and useJalaliCalendar is aligned to this convention -- the first column is always Saturday.
| Index | Day |
|---|---|
| 0 | شنبه (Saturday) |
| 1 | یکشنبه (Sunday) |
| 2 | دوشنبه (Monday) |
| 3 | سهشنبه (Tuesday) |
| 4 | چهارشنبه (Wednesday) |
| 5 | پنجشنبه (Thursday) |
| 6 | جمعه (Friday) |