---
layout: ~/layouts/MdxDocLayout.astro
title: "DatePicker"
description: "A date selection calendar supporting single, multiple, and range selection modes."
sourceFile: "components/date-picker"
---

import ComponentExample from "~/components/docs/ComponentExample.astro";
import ComponentSection from "~/components/docs/ComponentSection.astro";
import CodeBlock from "~/components/docs/CodeBlock.astro";
import Heading from "~/components/docs/Heading.astro";
import PropsTable from "~/components/docs/PropsTable.astro";
import {
  DatePickerSingleDemo,
  DatePickerMultipleDemo,
  DatePickerRangeDemo,
  DatePickerRangeMinMaxDemo,
  DatePickerPopoverDemo,
  DatePickerRangePopoverDemo,
  DatePickerRangeWithPresetsDemo,
  DatePickerDisabledWithFooterDemo,
} from "~/components/demos/DatePickerDemo";

{/* Demo */}

<ComponentSection>
  <ComponentExample
    code={`const [date, setDate] = useState<Date>();
<DatePicker mode="single" selected={date} onChange={setDate} />`}
  >
    <DatePickerSingleDemo client:load />
  </ComponentExample>
</ComponentSection>

{/* Installation */}

<ComponentSection>
  <Heading level={2}>Installation</Heading>
  <Heading level={3}>Barrel</Heading>
  <CodeBlock
    code={`import { DatePicker, type DateRange } from "@cloudflare/kumo";`}
    lang="tsx"
  />
  <Heading level={3}>Granular</Heading>
  <CodeBlock
    code={`import { DatePicker, type DateRange } from "@cloudflare/kumo/components/date-picker";`}
    lang="tsx"
  />
</ComponentSection>

{/* Usage */}

<ComponentSection>
  <Heading level={2}>Usage</Heading>
  <p>
    DatePicker supports three selection modes: `single`,
    `multiple`, and
    `range`.
  </p>
  <CodeBlock
    code={`import { useState } from "react";
import { DatePicker } from "@cloudflare/kumo";

export default function Example() {
  const [date, setDate] = useState<Date>();
  
  return (
    <DatePicker
      mode="single"
      selected={date}
      onChange={setDate}
    />
  );
}`}
    lang="tsx"
  />
</ComponentSection>

{/* Examples */}

<ComponentSection>
  <Heading level={2}>Examples</Heading>

{/* Single Selection */}

  <Heading level={3}>Single Date Selection</Heading>
  <p>
    Select a single date. The most common use case for date pickers.
  </p>
  <ComponentExample
    code={`const [date, setDate] = useState<Date>();

<DatePicker
  mode="single"
  selected={date}
  onChange={setDate}
/>`}
  >
    <DatePickerSingleDemo client:load />
  </ComponentExample>

{/* Multiple Selection */}

  <Heading level={3}>Multiple Date Selection</Heading>
  <p>
    Select multiple individual dates. Use `max` to limit the number of selections.
  </p>
  <ComponentExample
    code={`const [dates, setDates] = useState<Date[]>();

<DatePicker
  mode="multiple"
  selected={dates}
  onChange={setDates}
  max={5}
/>`}
  >
    <DatePickerMultipleDemo client:load />
  </ComponentExample>

{/* Range Selection */}

  <Heading level={3}>Date Range Selection</Heading>
  <p>
    Select a continuous range of dates. Works well with `numberOfMonths={2}` for a side-by-side view.
  </p>
  <ComponentExample
    code={`const [range, setRange] = useState<DateRange>();

<DatePicker
  mode="range"
  selected={range}
  onChange={setRange}
  numberOfMonths={2}
/>`}
  >
    <DatePickerRangeDemo client:load />
  </ComponentExample>

{/* Range with Min/Max */}

<Heading level={3}>Range with Min/Max Constraints</Heading>
<p>
  Constrain the range length using
  `min` and
  `max` props (in
  days/nights).
</p>
<ComponentExample
  code={`<DatePicker
  mode="range"
  selected={range}
  onChange={setRange}
  min={3}
  max={7}
  footer={<span>Select 3-7 nights</span>}
/>`}
>
  <DatePickerRangeMinMaxDemo client:load />
</ComponentExample>

{/* With Popover */}

<Heading level={3}>With Popover</Heading>
<p>
  Compose with the
  <a href="/components/popover" class="text-kumo-link hover:underline">
    Popover
  </a>
  component to create a dropdown date picker.
</p>
<ComponentExample
  code={`<Popover>
  <Popover.Trigger asChild>
    <Button variant="outline" icon={CalendarDotsIcon}>
      {date ? date.toLocaleDateString() : "Pick a date"}
    </Button>
  </Popover.Trigger>
  <Popover.Content className="p-1">
    <DatePicker
      mode="single"
      selected={date}
      onChange={(d) => setDate(d)}
    />
  </Popover.Content>
</Popover>`}
>
  <DatePickerPopoverDemo client:load />
</ComponentExample>

{/* Range with Popover */}

<Heading level={3}>Date Range with Popover</Heading>
<p>A date range picker in a popover with two months displayed.</p>
<ComponentExample
  code={`<Popover>
  <Popover.Trigger asChild>
    <Button variant="outline" icon={CalendarDotsIcon}>
      {formatRange()}
    </Button>
  </Popover.Trigger>
  <Popover.Content className="p-1">
    <DatePicker
      mode="range"
      selected={range}
      onChange={(r) => setRange(r)}
      numberOfMonths={2}
    />
  </Popover.Content>
</Popover>`}
>
  <DatePickerRangePopoverDemo client:load />
</ComponentExample>

{/* Range with Presets */}

<Heading level={3}>Date Range with Presets</Heading>
<p>Combine the date picker with preset options for quick selection.</p>
<ComponentExample
  code={`<Popover>
  <Popover.Trigger asChild>
    <Button variant="outline" icon={CalendarDotsIcon}>
      {formatRange()}
    </Button>
  </Popover.Trigger>
  <Popover.Content className="p-0">
    <div className="flex">
      <div className="flex flex-col gap-1 border-r border-kumo-line p-2 text-sm">
        {presets.map((preset) => (
          <button
            key={preset.label}
            onClick={() => setRange(preset.range)}
            className="rounded-md px-3 py-1.5 text-left hover:bg-kumo-control"
          >
            {preset.label}
          </button>
        ))}
      </div>
      <div className="p-1">
        <DatePicker
          mode="range"
          selected={range}
          onChange={(r) => setRange(r)}
          numberOfMonths={2}
        />
      </div>
    </div>
  </Popover.Content>
</Popover>`}
>
  <DatePickerRangeWithPresetsDemo client:load />
</ComponentExample>

{/* Disabled Dates with Footer */}

  <Heading level={3}>Disabled Dates with Usage Limits</Heading>
  <p>
    Use the `disabled` prop to make certain dates unselectable,
    and `footer` to display usage information.
  </p>
  <ComponentExample
    code={`const unavailableDates = [
  new Date(2025, 0, 5),
  new Date(2025, 0, 12),
];

<DatePicker
  mode="multiple"
  selected={dates}
  onChange={setDates}
  max={5}
  disabled={unavailableDates}
  footer={
    <p className="text-xs text-kumo-subtle pt-2">
      {selectedCount}/{maxDays} days selected. Grayed dates are unavailable.
    </p>
  }
/>`}
  >
    <DatePickerDisabledWithFooterDemo client:load />
  </ComponentExample>
</ComponentSection>

{/* Composing with Popover (Code Example) */}

<ComponentSection>
  <Heading level={2}>Full Popover Example</Heading>
  <p>
    Here's a complete example showing how to compose DatePicker with Popover:
  </p>
  <CodeBlock
    code={`import { useState } from "react";
import { DatePicker, Popover, Button } from "@cloudflare/kumo";
import { CalendarDotsIcon } from "@phosphor-icons/react";

export function DatePickerDropdown() {
  const [date, setDate] = useState<Date>();

return (

<Popover>
  <Popover.Trigger asChild>
    <Button variant="outline" icon={CalendarDotsIcon}>
      {date ? date.toLocaleDateString() : "Pick a date"}
    </Button>
  </Popover.Trigger>
  <Popover.Content className="p-3">
    <DatePicker mode="single" selected={date} onChange={(d) => setDate(d)} />
  </Popover.Content>
</Popover>
); }`} lang="tsx" />

</ComponentSection>

{/* API Reference */}

<ComponentSection>
  <Heading level={2}>API Reference</Heading>
  <p>
    DatePicker forwards all props to <a href="https://daypicker.dev/api/interfaces/PropsBase" class="text-kumo-link hover:underline" target="_blank" rel="noopener noreferrer">react-day-picker</a>. 
    Key props include:
  </p>
  <ul class="mt-4 list-disc list-inside text-kumo-strong space-y-2">
    <li>`mode` — <code class="text-xs">"single" | "multiple" | "range"</code> — Selection mode (required)</li>
    <li>`selected` — Currently selected date(s)</li>
    <li>`onChange` — Callback when selection changes</li>
    <li>`numberOfMonths` — Number of months to display</li>
    <li>`disabled` — Dates that cannot be selected</li>
    <li>`min` / `max` — Min/max selection constraints</li>
    <li>`footer` — Content rendered below the calendar</li>
    <li>`locale` — date-fns locale for internationalization</li>
    <li>`className` — Additional CSS classes</li>
  </ul>
  <p class="mt-4 text-kumo-subtle text-sm">
    See the <a href="https://daypicker.dev/docs" class="text-kumo-link hover:underline" target="_blank" rel="noopener noreferrer">react-day-picker documentation</a> for the full API.
  </p>

  <Heading level={3}>Differences from react-day-picker</Heading>
  <p>
    For consistency with other Kumo form components, DatePicker uses `onChange` instead of react-day-picker's `onSelect`.
    Full type inference is preserved — TypeScript will correctly narrow the callback signature based on the `mode` prop.
  </p>
</ComponentSection>
