Skip to content
Dethink Components

ComponentsCalendar

Calendar

Let users choose a date or date range from a month view.

On this page

Use this component from the local workspace package. Follow the setup guide first. A public npm package and registry are not available yet.

View registry files

Import the component into your page or component file.

Usage
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.

Launch date, July 2026

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
examples/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.

Sprint dates, July 2026

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
examples/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.

Delivery slot, July 2026

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/bounds.tsx
examples/calendar/bounds.tsx
"use client";

import { Calendar } from "@dethink/components";
import { CalendarDate, getDayOfWeek } from "@internationalized/date";

const minValue = new CalendarDate(2026, 7, 6);
const maxValue = new CalendarDate(2026, 7, 31);

export function CalendarBounds() {
  return (
    <div className="flex justify-center">
      <Calendar
        aria-label="Delivery slot"
        defaultFocusedValue={minValue}
        minValue={minValue}
        maxValue={maxValue}
        // Weekends are unavailable for delivery.
        isDateUnavailable={(date) => {
          const dayOfWeek = getDayOfWeek(date, "en-US");
          return dayOfWeek === 0 || dayOfWeek === 6;
        }}
      />
    </div>
  );
}

Locale and week start

locale drives month and weekday formatting, weekStartsOn overrides the first day of the week, and weekdayStyle switches the header format.

Date (German), Juli 2026

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

Date (Japanese), 2026年7月

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
examples/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.

Calendar and RangeCalendar props
PropWhat it doesDefault
valueDateValue | nullControlled selected date, built with @internationalized/date (for RangeCalendar: { start, end }).Not set
defaultValueDateValue | nullThe starting selection when the component manages its own state.Not set
onValueChange(value) => voidFires with the new date (or range) whenever the selection changes.Not set
minValue / maxValueDateValueInclusive bounds; dates outside them cannot be selected.Not set
isDateUnavailable(date: DateValue) => booleanMarks individual dates unavailable — rendered struck through and unselectable.Not set
disabledbooleanDisables the whole calendar.false
invalidbooleanMarks the current selection invalid for form validation.false
isReadOnlybooleanSelection is visible and focusable but cannot change (react-aria passthrough).false
localestringBCP 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"