Calendar
A calendar component that allows users to select a date or a range of dates.
Installation
Command
Manual
Use the JGuang UI Registry URL provided on /systems/jguang-ui#install.
Install the following dependencies:
npm install react-day-picker date-fns
Add the Button component to your project.
The Calendar component uses the Button component. Make sure you have it installed in your project.
Copy and paste the following code into your project.
View complete source: components/ui/calendar.tsx
Update the import paths to match your project setup.
Usage
import { Calendar } from "@/components/ui/calendar"
const [date, setDate] = React.useState<Date | undefined>(new Date())
return (
<Calendar
mode="single"
selected={date}
onSelect={setDate}
className="rounded-lg border"
/>
)
See the React DayPicker documentation for more information.
About
The Calendar component is built on top of React DayPicker.
Date Picker
You can use the <Calendar> component to build a date picker. See the Date Picker page for more information.
Persian / Hijri / Jalali Calendar
To use the Persian calendar, edit components/ui/calendar.tsx and replace react-day-picker with react-day-picker/persian.
- import { DayPicker } from "react-day-picker"
+ import { DayPicker } from "react-day-picker/persian"
Selected Date (With TimeZone)
The Calendar component accepts a timeZone prop to ensure dates are displayed and selected in the user's local timezone.
export function CalendarWithTimezone() {
const [date, setDate] = React.useState<Date | undefined>(undefined)
const [timeZone, setTimeZone] = React.useState<string | undefined>(undefined)
React.useEffect(() => {
setTimeZone(Intl.DateTimeFormat().resolvedOptions().timeZone)
}, [])
return (
<Calendar
mode="single"
selected={date}
onSelect={setDate}
timeZone={timeZone}
/>
)
}
Note: If you notice a selected date offset (for example, selecting the 20th highlights the 19th), make sure the timeZone prop is set to the user's local timezone.
Why client-side? The timezone is detected using Intl.DateTimeFormat().resolvedOptions().timeZone inside a useEffect to ensure compatibility with server-side rendering. Detecting the timezone during render would cause hydration mismatches, as the server and client may be in different timezones.
Basic
A basic calendar component. We used className="rounded-lg border" to style the calendar.
Range Calendar
Use the mode="range" prop to enable range selection.
Month and Year Selector
Use captionLayout="dropdown" to show month and year dropdowns.
Presets
Date and Time Picker
Booked dates
Custom Cell Size
You can customize the size of calendar cells using the --cell-size CSS variable. You can also make it responsive by using breakpoint-specific values:
<Calendar
mode="single"
selected={date}
onSelect={setDate}
className="rounded-lg border [--cell-size:--spacing(11)] md:[--cell-size:--spacing(12)]"
/>
Or use fixed values:
<Calendar
mode="single"
selected={date}
onSelect={setDate}
className="rounded-lg border [--cell-size:2.75rem] md:[--cell-size:3rem]"
/>
Week Numbers
Use showWeekNumber to show week numbers.
RTL
To enable RTL support in JGuang UI, see the RTL configuration guide.
See also the Hijri Guide for enabling the Persian / Hijri / Jalali calendar.
When using RTL, import the locale from react-day-picker/locale and pass both the locale and dir props to the Calendar component:
import { arSA } from "react-day-picker/locale"
;<Calendar
mode="single"
selected={date}
onSelect={setDate}
locale={arSA}
dir="rtl"
/>
API Reference
See the React DayPicker documentation for more information on the Calendar component.
Changelog
RTL Support
If you're upgrading from a previous version of the Calendar component, you'll need to apply the following updates to add locale support:
Import the Locale type.
Add Locale to your imports from react-day-picker:
import {
DayPicker,
getDefaultClassNames,
type DayButton,
+ type Locale,
} from "react-day-picker"
Add locale prop to the Calendar component.
Add the locale prop to the component's props:
function Calendar({
className,
classNames,
showOutsideDays = true,
captionLayout = "label",
buttonVariant = "ghost",
+ locale,
formatters,
components,
...props
}: React.ComponentProps<typeof DayPicker> & {
buttonVariant?: React.ComponentProps<typeof Button>["variant"]
}) {
Pass locale to DayPicker.
Pass the locale prop to the DayPicker component:
<DayPicker
showOutsideDays={showOutsideDays}
className={cn(...)}
captionLayout={captionLayout}
+ locale={locale}
formatters={{
formatMonthDropdown: (date) =>
- date.toLocaleString("default", { month: "short" }),
+ date.toLocaleString(locale?.code, { month: "short" }),
...formatters,
}}
Update CalendarDayButton to accept locale.
Update the CalendarDayButton component signature and pass locale:
function CalendarDayButton({
className,
day,
modifiers,
+ locale,
...props
- }: React.ComponentProps<typeof DayButton>) {
+ }: React.ComponentProps<typeof DayButton> & { locale?: Partial<Locale> }) {
Update date formatting in CalendarDayButton.
Use locale?.code in the date formatting:
<Button
variant="ghost"
size="icon"
- data-day={day.date.toLocaleDateString()}
+ data-day={day.date.toLocaleDateString(locale?.code)}
...
/>
Pass locale to DayButton component.
Update the DayButton component usage to pass the locale prop:
components={{
...
- DayButton: CalendarDayButton,
+ DayButton: ({ ...props }) => (
+ <CalendarDayButton locale={locale} {...props} />
+ ),
...
}}
Update RTL-aware CSS classes.
Replace directional classes with logical properties for better RTL support:
// In the day classNames:
- [&:last-child[data-selected=true]_button]:rounded-r-(--cell-radius)
+ [&:last-child[data-selected=true]_button]:rounded-e-(--cell-radius)
- [&:nth-child(2)[data-selected=true]_button]:rounded-l-(--cell-radius)
+ [&:nth-child(2)[data-selected=true]_button]:rounded-s-(--cell-radius)
- [&:first-child[data-selected=true]_button]:rounded-l-(--cell-radius)
+ [&:first-child[data-selected=true]_button]:rounded-s-(--cell-radius)
// In range_start classNames:
- rounded-l-(--cell-radius) ... after:right-0
+ rounded-s-(--cell-radius) ... after:end-0
// In range_end classNames:
- rounded-r-(--cell-radius) ... after:left-0
+ rounded-e-(--cell-radius) ... after:start-0
// In CalendarDayButton className:
- data-[range-end=true]:rounded-r-(--cell-radius)
+ data-[range-end=true]:rounded-e-(--cell-radius)
- data-[range-start=true]:rounded-l-(--cell-radius)
+ data-[range-start=true]:rounded-s-(--cell-radius)
After applying these changes, you can use the locale prop to provide locale-specific formatting:
import { enUS } from "react-day-picker/locale"
;<Calendar mode="single" selected={date} onSelect={setDate} locale={enUS} />
Source: components/base/calendar.mdx
Technical documentation for JGuang UI.