DatePicker
kumo-svelte
June 2026

Selected: None

<script lang="ts">
  import { DatePicker } from 'kumo-svelte';

  let date = $state<Date | undefined>();
</script>

<div class="flex flex-col gap-4">
  <DatePicker
    mode="single"
    selected={date}
    onChange={(d) => {
      if (d instanceof Date) {
        date = d;
      }
    }}
  />
  <p class="text-sm text-kumo-subtle">
    Selected: {date ? date.toLocaleDateString() : 'None'}
  </p>
</div>

Installation

Barrel

import { DatePicker } from 'kumo-svelte';

Granular

import { DatePicker } from 'kumo-svelte/components/date-picker';

Usage

DatePicker supports three selection modes: `single`, `multiple`, and `range`.

<script lang="ts">  import { DatePicker } from 'kumo-svelte';  let date = $state<Date | undefined>();</script><DatePicker mode="single" selected={date} onChange={(d) => (date = d as Date | undefined)} />

Examples

Single Date Selection

Select a single date. The most common use case for date pickers.

June 2026

Selected: None

<script lang="ts">
  import { DatePicker } from 'kumo-svelte';

  let date = $state<Date | undefined>();
</script>

<div class="flex flex-col gap-4">
  <DatePicker
    mode="single"
    selected={date}
    onChange={(d) => {
      if (d instanceof Date) {
        date = d;
      }
    }}
  />
  <p class="text-sm text-kumo-subtle">
    Selected: {date ? date.toLocaleDateString() : 'None'}
  </p>
</div>

Multiple Date Selection

Select multiple individual dates. Use `max` to limit the number of selections.

June 2026

Selected: 0 date(s)

<script lang="ts">
  import { DatePicker } from 'kumo-svelte';

  let dates = $state<Date[] | undefined>();
</script>

<div class="flex flex-col gap-4">
  <DatePicker
    mode="multiple"
    selected={dates}
    onChange={(d) => (dates = d as Date[] | undefined)}
    max={5}
  />
  <p class="text-sm text-kumo-subtle">
    Selected: {dates?.length ?? 0} date(s)
  </p>
</div>

Date Range Selection

Select a continuous range of dates. Works well with `numberOfMonths=2` for a side-by-side view.

June 2026
July 2026

Range: None

<script lang="ts">
  import { DatePicker, type DateRange } from 'kumo-svelte';

  let range = $state<DateRange | undefined>();
</script>

<div class="flex flex-col gap-4">
  <DatePicker
    mode="range"
    selected={range}
    onChange={(d) => (range = d as DateRange | undefined)}
    numberOfMonths={2}
  />
  <p class="text-sm text-kumo-subtle">
    Range: {range?.from
      ? `${range.from.toLocaleDateString()} - ${range.to?.toLocaleDateString() ?? '...'}`
      : 'None'}
  </p>
</div>

Range with Min/Max Constraints

Constrain the range length using `min` and `max` props (in days/nights).

June 2026
<script lang="ts">
  import { DatePicker, type DateRange } from 'kumo-svelte';

  let range = $state<DateRange | undefined>();
</script>

{#snippet footer()}
  <span class="text-xs text-kumo-subtle">Select 3-7 nights</span>
{/snippet}

<div class="flex flex-col gap-4">
  <DatePicker
    mode="range"
    selected={range}
    onChange={(d) => (range = d as DateRange | undefined)}
    min={3}
    max={7}
    {footer}
  />
</div>

With Popover

Compose with the [Popover](/components/popover) component to create a dropdown date picker.

<script lang="ts">
  import { Button, DatePicker, Popover } from 'kumo-svelte';
  import { CalendarDotsIcon } from 'phosphor-svelte';

  let date = $state<Date | undefined>();
</script>

<Popover class="p-3">
  {#snippet trigger(props)}
    <Button variant="outline" icon={CalendarDotsIcon} {...props}>
      {date ? date.toLocaleDateString() : 'Pick a date'}
    </Button>
  {/snippet}

  <DatePicker mode="single" selected={date} onChange={(d) => (date = d as Date | undefined)} />
</Popover>

Date Range with Popover

A date range picker in a popover with two months displayed.

<script lang="ts">
  import { Button, DatePicker, Popover, type DateRange } from 'kumo-svelte';
  import { CalendarDotsIcon } from 'phosphor-svelte';

  let range = $state<DateRange | undefined>();

  function formatRange() {
    if (!range?.from) return 'Select dates';
    if (!range.to) return range.from.toLocaleDateString();
    return `${range.from.toLocaleDateString()} - ${range.to.toLocaleDateString()}`;
  }
</script>

<Popover class="p-3">
  {#snippet trigger(props)}
    <Button variant="outline" icon={CalendarDotsIcon} {...props}>
      {formatRange()}
    </Button>
  {/snippet}

  <DatePicker
    mode="range"
    selected={range}
    onChange={(d) => (range = d as DateRange | undefined)}
    numberOfMonths={2}
  />
</Popover>

Date Range with Presets

Combine the date picker with preset options for quick selection.

<script lang="ts">
  import { Button, DatePicker, Popover, type DateRange } from 'kumo-svelte';
  import { CalendarDotsIcon } from 'phosphor-svelte';

  let range = $state<DateRange | undefined>();
  let month = $state(new Date());
  const today = new Date();

  const presets = [
    { label: 'Today', range: { from: today, to: today } },
    {
      label: 'Last 7 days',
      range: { from: new Date(today.getTime() - 6 * 24 * 60 * 60 * 1000), to: today }
    },
    {
      label: 'Last 30 days',
      range: { from: new Date(today.getTime() - 29 * 24 * 60 * 60 * 1000), to: today }
    },
    {
      label: 'Last 90 days',
      range: { from: new Date(today.getTime() - 89 * 24 * 60 * 60 * 1000), to: today }
    },
    {
      label: 'This month',
      range: { from: new Date(today.getFullYear(), today.getMonth(), 1), to: new Date(today.getFullYear(), today.getMonth() + 1, 0) }
    },
    {
      label: 'Last month',
      range: { from: new Date(today.getFullYear(), today.getMonth() - 1, 1), to: new Date(today.getFullYear(), today.getMonth(), 0) }
    }
  ];

  function formatRange() {
    if (!range?.from) return 'Select dates';
    if (!range.to) return range.from.toLocaleDateString();
    return `${range.from.toLocaleDateString()} - ${range.to.toLocaleDateString()}`;
  }

  function handlePresetClick(preset: { range: DateRange }) {
    range = preset.range;
    if (preset.range.from) month = preset.range.from;
  }

  function isPresetActive(preset: { range: DateRange }) {
    if (!range?.from || !range?.to || !preset.range.from || !preset.range.to) return false;
    return range.from.toDateString() === preset.range.from.toDateString()
      && range.to.toDateString() === preset.range.to.toDateString();
  }
</script>

<Popover class="p-0">
  {#snippet trigger(props)}
    <Button variant="outline" icon={CalendarDotsIcon} {...props}>{formatRange()}</Button>
  {/snippet}

  <div class="flex">
    <div class="flex flex-col gap-1 border-r border-kumo-hairline p-2 text-sm">
      {#each presets as preset (preset.label)}
        <button
          type="button"
          onclick={() => handlePresetClick(preset)}
          class={isPresetActive(preset)
            ? 'rounded-md bg-kumo-bg-inverse px-3 py-1.5 text-left whitespace-nowrap text-kumo-text-inverse'
            : 'rounded-md px-3 py-1.5 text-left whitespace-nowrap text-kumo-subtle hover:bg-kumo-control'}
        >
          {preset.label}
        </button>
      {/each}
    </div>
    <div class="p-3">
      <DatePicker
        mode="range"
        selected={range}
        onChange={(d) => (range = d as DateRange | undefined)}
        bind:month
        numberOfMonths={2}
      />
    </div>
  </div>
</Popover>

Disabled Dates with Usage Limits

Use the `disabled` prop to make certain dates unselectable, and `footer` to display usage information.

June 2026
<script lang="ts">
  import { DatePicker } from 'kumo-svelte';

  let dates = $state<Date[] | undefined>();
  const today = new Date();
  const maxDays = 5;
  const unavailableDates = [
    new Date(today.getFullYear(), today.getMonth(), 5),
    new Date(today.getFullYear(), today.getMonth(), 12),
    new Date(today.getFullYear(), today.getMonth(), 18),
    new Date(today.getFullYear(), today.getMonth(), 25)
  ];
</script>

{#snippet footer()}
  <p class="w-full pt-2 text-xs text-kumo-subtle">
    {dates?.length ?? 0}/{maxDays} days selected. Grayed dates are unavailable.
  </p>
{/snippet}

<DatePicker
  mode="multiple"
  selected={dates}
  onChange={(d) => (dates = d as Date[] | undefined)}
  max={maxDays}
  disabled={unavailableDates}
  fixedWeeks
  {footer}
/>

Full Popover Example

Here's a complete example showing how to compose DatePicker with Popover:

<script lang="ts">  import { Button, DatePicker, Popover } from 'kumo-svelte';  import { CalendarDotsIcon } from 'phosphor-svelte';  let date = $state<Date | undefined>();</script><Popover class="p-3">  {#snippet trigger(props)}    <Button variant="outline" icon={CalendarDotsIcon} {...props}>      {date ? date.toLocaleDateString() : 'Pick a date'}    </Button>  {/snippet}  <DatePicker mode="single" selected={date} onChange={(d) => (date = d as Date | undefined)} /></Popover>

API Reference

DatePicker mirrors the original Kumo calendar API. Key props include:

  • `mode` — "single" | "multiple" | "range" — Selection mode (required)
  • `selected` — Currently selected date(s)
  • `onChange` — Callback when selection changes
  • `numberOfMonths` — Number of months to display
  • `disabled` — Dates that cannot be selected
  • `min` / `max` — Min/max selection constraints
  • `footer` — Content rendered below the calendar
  • `locale` — Locale string for internationalization
  • `className` — Additional CSS classes

See the react-day-picker documentation for the full upstream API.

Differences from react-day-picker

For consistency with other Kumo form components, DatePicker uses `onChange` instead of react-day-picker's `onSelect`.