Skip to content
Dethink Components

ComponentsDateRangePicker

DateRangePicker

Let users choose a start date and an end date.

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 {
  DateRangePicker,
  type DateRangePickerValue,
} from "@dethink/components";
import { CalendarDate } from "@internationalized/date";

Try the examples, then open the code to use them in your app. Tab through the start and end segments, or open the popover and pick both ends on the range calendar.

Basic

Label and helper description around the linked start–end field — the uncontrolled default.

Trip dates
Both nights are included in the rate.
Show sourceexamples/date-range-picker/basic.tsx
examples/date-range-picker/basic.tsx
"use client";

import { DateRangePicker } from "@dethink/components";

export function DateRangePickerBasic() {
  return (
    <div className="mx-auto max-w-sm">
      <DateRangePicker
        label="Trip dates"
        description="Both nights are included in the rate."
      />
    </div>
  );
}

Controlled and clearable

value and onValueChange hold the { start, end } pair in React state; clearable resets the whole range at once.

Sprint window

2026-07-06 → 2026-07-17

Show sourceexamples/date-range-picker/controlled.tsx
examples/date-range-picker/controlled.tsx
"use client";

import { useState } from "react";
import {
  DateRangePicker,
  type DateRangePickerValue,
} from "@dethink/components";
import { CalendarDate } from "@internationalized/date";

export function DateRangePickerControlled() {
  const [value, setValue] = useState<DateRangePickerValue | null>({
    start: new CalendarDate(2026, 7, 6),
    end: new CalendarDate(2026, 7, 17),
  });

  return (
    <div className="mx-auto max-w-sm space-y-3">
      <DateRangePicker
        label="Sprint window"
        value={value}
        onValueChange={setValue}
        clearable
      />
      <p className="text-muted-foreground text-sm">
        {value
          ? `${value.start.toString()} → ${value.end.toString()}`
          : "No range selected"}
      </p>
    </div>
  );
}

Native form submission

startName and endName name the hidden inputs, so a plain FormData read gets both ISO dates.

Stay
Show sourceexamples/date-range-picker/form.tsx
examples/date-range-picker/form.tsx
"use client";

import { useState } from "react";
import { Button, DateRangePicker } from "@dethink/components";

export function DateRangePickerForm() {
  const [submitted, setSubmitted] = useState<string | null>(null);

  return (
    <form
      className="mx-auto max-w-sm space-y-3"
      onSubmit={(event) => {
        event.preventDefault();
        const data = new FormData(event.currentTarget);
        setSubmitted(
          `checkIn=${data.get("checkIn")} checkOut=${data.get("checkOut")}`,
        );
      }}
    >
      <DateRangePicker
        label="Stay"
        startName="checkIn"
        endName="checkOut"
        required
      />
      <Button type="submit" size="sm">
        Book
      </Button>
      {submitted ? (
        <p className="text-muted-foreground font-mono text-xs">{submitted}</p>
      ) : null}
    </form>
  );
}

Bounds

minValue and maxValue clamp both ends of the range to a window.

Q3 review period
Must fall inside the third quarter.
Show sourceexamples/date-range-picker/bounds.tsx
examples/date-range-picker/bounds.tsx
"use client";

import { DateRangePicker } from "@dethink/components";
import { CalendarDate } from "@internationalized/date";

export function DateRangePickerBounds() {
  return (
    <div className="mx-auto max-w-sm">
      <DateRangePicker
        label="Q3 review period"
        description="Must fall inside the third quarter."
        minValue={new CalendarDate(2026, 7, 1)}
        maxValue={new CalendarDate(2026, 9, 30)}
        defaultValue={{
          start: new CalendarDate(2026, 7, 6),
          end: new CalendarDate(2026, 7, 17),
        }}
      />
    </div>
  );
}

DateRangePicker owns its field anatomy — pass a label instead of wrapping it in an external one.

DateRangePicker props
PropWhat it doesDefault
valueDateRangePickerValue | nullControlled range — { start, end } CalendarDates from @internationalized/date.Not set
defaultValueDateRangePickerValue | nullInitial range for uncontrolled usage.Not set
onValueChange(value: DateRangePickerValue | null) => voidFires when the range changes or is cleared.Not set
label / description / errorMessageReactNodeThe field label, help text, and error message.Not set
namestringBase name for the hidden inputs — submits as "<name>Start" and "<name>End".Not set
startName / endNamestringExplicit hidden input names for the start and end dates; override the base name.Not set
required / disabled / readOnly / invalidbooleanMakes the field required, disabled, read-only, or invalid.false
clearablebooleanShows a clear button when a range is selected.false
minValue / maxValueDateValueInclusive bounds enforced across both ends of the range.Not set
isDateUnavailable(date: DateValue) => booleanMarks individual dates unavailable in the popover calendar.Not set
localestringBCP 47 tag controlling segment order and formatting.runtime locale
weekStartsOn"sun" | "mon" | … | "sat"First day of the week in the popover calendar.locale default