Trip dates, 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 { RangeCalendar } from "@/components/ui/preskok-ui/range-calendar"
export function RangeCalendarPreskokDemo() {
return <RangeCalendar aria-label="Trip dates" />
}
Installation
CLI
pnpmnpmyarnbunpnpm dlx @preskok-org/ui@latest add range-calendar
Usage
import { parseDate } from "@internationalized/date"
import { RangeCalendar } from "@/registry/preskok/ui/preskok-ui/range-calendar"
export function RangeCalendarDemo() {
return (
<RangeCalendar
defaultValue={{
start: parseDate("2026-04-10"),
end: parseDate("2026-04-16"),
}}
aria-label="Trip dates"
/>
)
}Props
- visibleDuration:
DateDuration— Number of months shown at once. - Inherits all
react-aria-componentsRangeCalendarprops.
Features
- Range-first interaction with contiguous date highlighting.
- Single or multi-month layouts with built-in navigation.
- Date constraints via
minValue,maxValue, andisDateUnavailable. - Keyboard and screen reader support from React Aria primitives.
Examples
Basic
Trip dates, 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 { RangeCalendar } from "@/components/ui/preskok-ui/range-calendar"
export function RangeCalendarPreskokDemo() {
return <RangeCalendar aria-label="Trip dates" />
}
Controlled
Controlled trip dates, 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 |
Selected: Apr 10 - Apr 16
"use client"
import { useState } from "react"
import type { CalendarDate } from "@internationalized/date"
import { getLocalTimeZone, parseDate } from "@internationalized/date"
import type { RangeValue } from "react-aria-components"
import { RangeCalendar } from "@/components/ui/preskok-ui/range-calendar"
export function RangeCalendarControlledPreskokDemo() {
const timeZone = getLocalTimeZone()
const [range, setRange] = useState<RangeValue<CalendarDate> | null>(() => ({
start: parseDate("2026-04-10"),
end: parseDate("2026-04-16"),
}))
const selectedRange =
range?.start && range?.end
? `${range.start.toDate(timeZone).toLocaleDateString("en-US", {
day: "numeric",
month: "short",
})} - ${range.end.toDate(timeZone).toLocaleDateString("en-US", {
day: "numeric",
month: "short",
})}`
: "No range selected"
return (
<div className="space-y-3">
<RangeCalendar
value={range}
onChange={setRange}
aria-label="Controlled trip dates"
/>
<p className="text-muted-foreground text-sm">Selected: {selectedRange}</p>
</div>
)
}
Disabled
Disabled trip dates, May 2026
May 2026
| 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 RangeCalendarDisabledPreskokDemo() {
return (
<RangeCalendar
defaultValue={{
start: parseDate("2026-05-03"),
end: parseDate("2026-05-08"),
}}
isDisabled
aria-label="Disabled trip dates"
/>
)
}
Validation
Validated trip dates, 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 |
Range must stay within 2 months and excludes weekends.
"use client"
import { useState } from "react"
import type { CalendarDate } from "@internationalized/date"
import { getLocalTimeZone, today } from "@internationalized/date"
import type { DateValue, RangeValue } from "react-aria-components"
import { RangeCalendar } from "@/components/ui/preskok-ui/range-calendar"
export function RangeCalendarValidationPreskokDemo() {
const timeZone = getLocalTimeZone()
const [range, setRange] = useState<RangeValue<CalendarDate> | null>(() => ({
start: today(timeZone).add({ days: 2 }),
end: today(timeZone).add({ days: 5 }),
}))
const minTripDate = today(timeZone)
const maxTripDate = minTripDate.add({ months: 2 })
const isWeekend = (value: DateValue) => {
const day = value.toDate(timeZone).getDay()
return day === 0 || day === 6
}
return (
<div className="space-y-3">
<RangeCalendar
value={range}
onChange={setRange}
minValue={minTripDate}
maxValue={maxTripDate}
isDateUnavailable={isWeekend}
aria-label="Validated trip dates"
/>
<p className="text-muted-foreground text-sm">
Range must stay within 2 months and excludes weekends.
</p>
</div>
)
}