Select a date, March 2026
March 2026
| S | M | T | W | T | F | S |
|---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 | 1 | 2 | 3 | 4 |
"use client"
import { useState } from "react"
import { getLocalTimeZone, today } from "@internationalized/date"
import { Calendar } from "@/components/ui/preskok-ui/calendar"
export function CalendarPreskokDemo() {
const [date, setDate] = useState(() => today(getLocalTimeZone()))
return (
<div className="flex justify-center">
<Calendar value={date} onChange={setDate} aria-label="Select a date" />
</div>
)
}
Installation
pnpmnpmyarnbunpnpm dlx @preskok-org/ui@latest add calendar
Usage
import { getLocalTimeZone, today } from "@internationalized/date"
import { Calendar } from "@/registry/preskok/ui/preskok-ui/calendar"
export function CalendarDemo() {
const [date, setDate] = useState(today(getLocalTimeZone()))
return <Calendar value={date} onChange={setDate} aria-label="Select a date" />
}Props
| Prop | Type | Default | Description |
|---|---|---|---|
value | DateValue | null | - | Selected date value |
onChange | (date: DateValue | null) => void | - | Date change handler |
minValue | DateValue | - | Minimum selectable date |
maxValue | DateValue | - | Maximum selectable date |
isDisabled | boolean | false | Disable the entire calendar |
isReadOnly | boolean | false | Make calendar read-only |
isDateUnavailable | (date: DateValue) => boolean | - | Function to disable specific dates |
errorMessage | string | - | Error message to display |
className | string | - | Additional CSS classes |
Examples
Basic
Select a date, March 2026
March 2026
| S | M | T | W | T | F | S |
|---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 | 1 | 2 | 3 | 4 |
"use client"
import { useState } from "react"
import { getLocalTimeZone, today } from "@internationalized/date"
import { Calendar } from "@/components/ui/preskok-ui/calendar"
export function CalendarPreskokDemo() {
const [date, setDate] = useState(() => today(getLocalTimeZone()))
return (
<div className="flex justify-center">
<Calendar value={date} onChange={setDate} aria-label="Select a date" />
</div>
)
}
Controlled
Controlled date, March 2026
March 2026
| S | M | T | W | T | F | S |
|---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 | 1 | 2 | 3 | 4 |
Selected: Mar 5, 2026
"use client"
import { useState } from "react"
import { getLocalTimeZone, today } from "@internationalized/date"
import type { DateValue } from "react-aria-components"
import { Calendar } from "@/components/ui/preskok-ui/calendar"
export function CalendarControlledPreskokDemo() {
const timeZone = getLocalTimeZone()
const [date, setDate] = useState(() => today(timeZone))
const selectedDate = (value: DateValue | null | undefined) =>
value?.toDate(timeZone).toLocaleDateString("en-US", {
day: "numeric",
month: "short",
year: "numeric",
}) ?? "No date selected"
return (
<div className="space-y-3">
<Calendar value={date} onChange={setDate} aria-label="Controlled date" />
<p className="text-muted-foreground text-sm">
Selected: {selectedDate(date)}
</p>
</div>
)
}
Controlled Month/Year
Select date with controlled focus, March 2026
March 2026
| S | M | T | W | T | F | S |
|---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 | 1 | 2 | 3 | 4 |
"use client"
import { useState } from "react"
import type { CalendarDate } from "@internationalized/date"
import { getLocalTimeZone, today } from "@internationalized/date"
import { Button } from "@/components/ui/preskok-ui/button"
import { Calendar } from "@/components/ui/preskok-ui/calendar"
export function CalendarControlledMonthYearPreskokDemo() {
const timeZone = getLocalTimeZone()
const [date, setDate] = useState<CalendarDate | null>(null)
const [focusedDate, setFocusedDate] = useState(() => today(timeZone))
return (
<div className="space-y-3">
<Calendar
value={date}
onChange={setDate}
focusedValue={focusedDate}
onFocusChange={setFocusedDate}
aria-label="Select date with controlled focus"
/>
<Button
intent="outline"
size="sm"
onPress={() => setFocusedDate(today(timeZone))}
>
Jump to current month
</Button>
</div>
)
}
Disabled
Disabled calendar, March 2026
March 2026
| S | M | T | W | T | F | S |
|---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 | 1 | 2 | 3 | 4 |
"use client"
import { getLocalTimeZone, today } from "@internationalized/date"
import { Calendar } from "@/components/ui/preskok-ui/calendar"
export function CalendarDisabledPreskokDemo() {
return (
<Calendar
defaultValue={today(getLocalTimeZone()).add({ days: 5 })}
isDisabled
aria-label="Disabled calendar"
/>
)
}
Unavailable Dates
Select a weekday, March 2026
March 2026
| S | M | T | W | T | F | S |
|---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 | 1 | 2 | 3 | 4 |
"use client"
import { useState } from "react"
import { getLocalTimeZone, today } from "@internationalized/date"
import type { DateValue } from "react-aria-components"
import { Calendar } from "@/components/ui/preskok-ui/calendar"
export function CalendarWithDisabledDatesPreskokDemo() {
const timeZone = getLocalTimeZone()
const [date, setDate] = useState(() => today(timeZone))
const isDateUnavailable = (value: DateValue) => {
const day = value.toDate(timeZone).getDay()
return day === 0 || day === 6
}
return (
<Calendar
value={date}
onChange={setDate}
isDateUnavailable={isDateUnavailable}
aria-label="Select a weekday"
/>
)
}
Min/Max Dates
Select future date, March 2026
March 2026
| S | M | T | W | T | F | S |
|---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 | 1 | 2 | 3 | 4 |
"use client"
import { useState } from "react"
import { getLocalTimeZone, today } from "@internationalized/date"
import { Calendar } from "@/components/ui/preskok-ui/calendar"
export function CalendarMinMaxPreskokDemo() {
const timeZone = getLocalTimeZone()
const minDate = today(timeZone)
const maxDate = minDate.add({ months: 3 })
const [date, setDate] = useState(() => minDate)
return (
<Calendar
value={date}
onChange={setDate}
minValue={minDate}
maxValue={maxDate}
aria-label="Select future date"
/>
)
}
Validation
Validated booking date, March 2026
March 2026
| S | M | T | W | T | F | S |
|---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 | 1 | 2 | 3 | 4 |
Select a weekday within the next 21 days.
"use client"
import { useState } from "react"
import { getLocalTimeZone, today } from "@internationalized/date"
import type { DateValue } from "react-aria-components"
import { Calendar } from "@/components/ui/preskok-ui/calendar"
export function CalendarValidationPreskokDemo() {
const timeZone = getLocalTimeZone()
const [date, setDate] = useState(() => today(timeZone).add({ days: 1 }))
const minBookingDate = today(timeZone)
const maxBookingDate = minBookingDate.add({ days: 21 })
const isWeekend = (value: DateValue) => {
const day = value.toDate(timeZone).getDay()
return day === 0 || day === 6
}
return (
<div className="space-y-3">
<Calendar
value={date}
onChange={setDate}
minValue={minBookingDate}
maxValue={maxBookingDate}
isDateUnavailable={isWeekend}
aria-label="Validated booking date"
/>
<p className="text-muted-foreground text-sm">
Select a weekday within the next 21 days.
</p>
</div>
)
}
Date Range
Select date range, April 2026
April 2026
| S | M | T | W | T | F | S |
|---|---|---|---|---|---|---|
29 | 30 | 31 | 1 | 2 | 3 | 4 |
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 1 | 2 |
"use client"
import { parseDate } from "@internationalized/date"
import { RangeCalendar } from "@/components/ui/preskok-ui/range-calendar"
export function CalendarWithRangePreskokDemo() {
return (
<RangeCalendar
defaultValue={{
start: parseDate("2026-04-10"),
end: parseDate("2026-04-16"),
}}
aria-label="Select date range"
/>
)
}
Multiple Months
Select trip dates, April to May 2026
April – May 2026
| S | M | T | W | T | F | S |
|---|---|---|---|---|---|---|
29 | 30 | 31 | 1 | 2 | 3 | 4 |
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 1 | 2 |
| S | M | T | W | T | F | S |
|---|---|---|---|---|---|---|
26 | 27 | 28 | 29 | 30 | 1 | 2 |
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
31 | 1 | 2 | 3 | 4 | 5 | 6 |
"use client"
import { parseDate } from "@internationalized/date"
import { RangeCalendar } from "@/components/ui/preskok-ui/range-calendar"
export function CalendarMultipleMonthsPreskokDemo() {
return (
<RangeCalendar
defaultValue={{
start: parseDate("2026-04-10"),
end: parseDate("2026-04-18"),
}}
visibleDuration={{ months: 2 }}
aria-label="Select trip dates"
/>
)
}
Features
- Single date selection - Select one date with clear focus and state handling
- Month/year navigation - Dropdown selectors for quick navigation
- Date validation - Min/max dates and custom unavailable dates
- Internationalization - Full i18n support with locale-aware formatting
- Keyboard navigation - Complete keyboard accessibility
- Custom styling - Extensive customization options
- Time zones - Proper time zone handling with @internationalized/date
- Screen reader support - Comprehensive accessibility features
Accessibility
The Calendar component follows WAI-ARIA guidelines:
- Proper grid structure with row/column headers
- Keyboard navigation with arrow keys
- Focus management and visual indicators
- Screen reader announcements for date changes
- Support for disabled and unavailable dates