ComponentsCalendar
Calendar
Let users choose a date or date range from a month view.
On this page
Install component source from the public registry, or use the local workspace package. Follow the setup guide first. The npm package is not published yet.
View registry files- Internationalized Date
(@internationalized/date)Date values, calendar arithmetic, and time-zone-aware date handling.
- React Aria Components
(react-aria-components)Accessible interaction primitives, including keyboard behavior, focus handling, and ARIA semantics.
Shared foundation: React, clsx for conditional classes, tailwind-merge for resolving utility-class conflicts. Styles use Tailwind CSS and Dethink CSS variables.
Based on this component’s source and registry composition. Libraries used only in showcase examples are excluded.
Import the component into your page or component file.
import { Calendar, RangeCalendar } from "@dethink/components";
import { CalendarDate } from "@internationalized/date";Try the examples, then open the code to use them in your app. Move focus into a grid and navigate with the arrow keys, PageUp/PageDown for months, and Enter or Space to select.
Single date
A controlled Calendar — value and onValueChange pair with React state holding a CalendarDate.
| Mon | Tue | Wed | Thu | Fri | Sat | Sun |
|---|---|---|---|---|---|---|
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 |
Selected: 2026-07-14
Show sourceexamples/calendar/basic.tsx
"use client";
import { useState } from "react";
import { Calendar } from "@dethink/components";
import { CalendarDate, type DateValue } from "@internationalized/date";
export function CalendarBasic() {
const [value, setValue] = useState<DateValue | null>(
new CalendarDate(2026, 7, 14),
);
return (
<div className="flex flex-col items-center gap-3">
<Calendar
aria-label="Launch date"
value={value}
onValueChange={setValue}
/>
<p className="text-muted-foreground text-sm">
Selected: {value ? value.toString() : "none"}
</p>
</div>
);
}Range
RangeCalendar selects a start and end date in one grid; the value is a { start, end } pair.
| Mon | Tue | Wed | Thu | Fri | Sat | Sun |
|---|---|---|---|---|---|---|
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 |
Show sourceexamples/calendar/range.tsx
"use client";
import { RangeCalendar } from "@dethink/components";
import { CalendarDate } from "@internationalized/date";
export function CalendarRange() {
return (
<div className="flex justify-center">
<RangeCalendar
aria-label="Sprint dates"
defaultValue={{
start: new CalendarDate(2026, 7, 6),
end: new CalendarDate(2026, 7, 17),
}}
/>
</div>
);
}Bounds and unavailable dates
minValue and maxValue clamp selection to a window, while isDateUnavailable strikes out individual dates — here, weekends.
| Mon | Tue | Wed | Thu | Fri | Sat | Sun |
|---|---|---|---|---|---|---|
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 |
Locale and week start
locale drives month and weekday formatting, weekStartsOn overrides the first day of the week, and weekdayStyle switches the header format.
| Mo | Di | Mi | Do | Fr | Sa | So |
|---|---|---|---|---|---|---|
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 |
| 日 | 月 | 火 | 水 | 木 | 金 | 土 |
|---|---|---|---|---|---|---|
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 |
Show sourceexamples/calendar/locale.tsx
"use client";
import { Calendar } from "@dethink/components";
import { CalendarDate } from "@internationalized/date";
export function CalendarLocale() {
return (
<div className="flex flex-wrap items-start justify-center gap-6">
<Calendar
aria-label="Date (German)"
locale="de-DE"
weekStartsOn="mon"
defaultValue={new CalendarDate(2026, 7, 14)}
/>
<Calendar
aria-label="Date (Japanese)"
locale="ja-JP"
weekdayStyle="narrow"
defaultValue={new CalendarDate(2026, 7, 14)}
/>
</div>
);
}Calendar and RangeCalendar share the same API; RangeCalendar's value shape is { start, end }. Always give each calendar an aria-label (or aria-labelledby) describing what is being picked.
| Prop | What it does | Default |
|---|---|---|
valueDateValue | null | Controlled selected date, built with @internationalized/date (for RangeCalendar: { start, end }). | Not set |
defaultValueDateValue | null | The starting selection when the component manages its own state. | Not set |
onValueChange(value) => void | Fires with the new date (or range) whenever the selection changes. | Not set |
minValue / maxValueDateValue | Inclusive bounds; dates outside them cannot be selected. | Not set |
isDateUnavailable(date: DateValue) => boolean | Marks individual dates unavailable — rendered struck through and unselectable. | Not set |
disabledboolean | Disables the whole calendar. | false |
invalidboolean | Marks the current selection invalid for form validation. | false |
isReadOnlyboolean | Selection is visible and focusable but cannot change (react-aria passthrough). | false |
localestring | BCP 47 tag (for example "de-DE") controlling month/weekday formatting. | runtime locale |
weekStartsOn"sun" | "mon" | … | "sat" | First day of the week, overriding the locale. | locale default |
weekdayStyle"narrow" | "short" | "long" | Weekday header format. | "short" |