| Su | Mo | Tu | We | Th | Fr | Sa |
|---|---|---|---|---|---|---|
Selected: None
<script lang="ts">
import type { DateValue } from "@internationalized/date";
import { getLocalTimeZone } from "@internationalized/date";
import { DatePicker } from "kumo-svelte/components/date-picker";
let date = $state<DateValue | undefined>();
function formatDate(value: DateValue | undefined) {
return value ? value.toDate(getLocalTimeZone()).toLocaleDateString() : "None";
}
</script>
<div class="flex flex-col gap-4">
<DatePicker mode="single" bind:value={date} />
<p class="text-sm text-kumo-subtle">Selected: {formatDate(date)}</p>
</div>
Import
import { DatePicker, type DatePickerValue } from "kumo-svelte/components/date-picker"; Usage
DatePicker supports three selection modes: single, multiple, and range. The Svelte component uses DateValue from @internationalized/date; range values use Bits UI’s { start, end } shape.
<script lang="ts">
import type { DateValue } from "@internationalized/date";
import { DatePicker } from "kumo-svelte/components/date-picker";
let date = $state<DateValue | undefined>();
</script>
<DatePicker mode="single" bind:value={date} /> Examples
Single Date Selection
Select a single date. The most common use case for date pickers.
| Su | Mo | Tu | We | Th | Fr | Sa |
|---|---|---|---|---|---|---|
Selected: None
<script lang="ts">
import type { DateValue } from "@internationalized/date";
import { getLocalTimeZone } from "@internationalized/date";
import { DatePicker } from "kumo-svelte/components/date-picker";
let date = $state<DateValue | undefined>();
function formatDate(value: DateValue | undefined) {
return value ? value.toDate(getLocalTimeZone()).toLocaleDateString() : "None";
}
</script>
<div class="flex flex-col gap-4">
<DatePicker mode="single" bind:value={date} />
<p class="text-sm text-kumo-subtle">Selected: {formatDate(date)}</p>
</div>
Multiple Date Selection
Select multiple individual dates. Use maxDays to limit the number of selections.
| Su | Mo | Tu | We | Th | Fr | Sa |
|---|---|---|---|---|---|---|
Selected: 0 date(s)
<script lang="ts">
import type { DateValue } from "@internationalized/date";
import { DatePicker } from "kumo-svelte/components/date-picker";
let dates = $state<DateValue[] | undefined>();
</script>
<div class="flex flex-col gap-4">
<DatePicker mode="multiple" bind:value={dates} maxDays={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. Use numberOfMonths={2} for a side-by-side view.
| Su | Mo | Tu | We | Th | Fr | Sa |
|---|---|---|---|---|---|---|
| Su | Mo | Tu | We | Th | Fr | Sa |
|---|---|---|---|---|---|---|
Range: None
<script lang="ts">
import type { DateValue } from "@internationalized/date";
import { getLocalTimeZone } from "@internationalized/date";
import { DatePicker } from "kumo-svelte/components/date-picker";
type DateRangeValue = { start: DateValue | undefined; end: DateValue | undefined };
let range = $state<DateRangeValue | undefined>();
function formatRange(value: DateRangeValue | undefined) {
if (!value?.start) return "None";
const start = value.start.toDate(getLocalTimeZone()).toLocaleDateString();
const end = value.end?.toDate(getLocalTimeZone()).toLocaleDateString() ?? "…";
return `${start} – ${end}`;
}
</script>
<div class="flex flex-col gap-4">
<DatePicker mode="range" bind:value={range} numberOfMonths={2} />
<p class="text-sm text-kumo-subtle">Range: {formatRange(range)}</p>
</div>
Range with Min/Max Constraints
Constrain the range length using minDays and maxDays props (in days/nights).
| Su | Mo | Tu | We | Th | Fr | Sa |
|---|---|---|---|---|---|---|
<script lang="ts">
import type { DateValue } from "@internationalized/date";
import { DatePicker } from "kumo-svelte/components/date-picker";
type DateRangeValue = { start: DateValue | undefined; end: DateValue | undefined };
let range = $state<DateRangeValue | undefined>();
</script>
{#snippet footer()}
<span class="text-xs text-kumo-subtle">Select 3-7 nights</span>
{/snippet}
<DatePicker mode="range" bind:value={range} minDays={3} maxDays={7} {footer} />
With Popover
Compose with the Popover component to create a dropdown date picker.
<script lang="ts">
import type { DateValue } from "@internationalized/date";
import { getLocalTimeZone } from "@internationalized/date";
import { Button } from "kumo-svelte/components/button";
import { DatePicker } from "kumo-svelte/components/date-picker";
import * as Popover from "kumo-svelte/components/popover";
import CalendarDotsIcon from "phosphor-svelte/lib/CalendarDotsIcon";
let date = $state<DateValue | undefined>();
function formatDate(value: DateValue | undefined) {
return value ? value.toDate(getLocalTimeZone()).toLocaleDateString() : "Pick a date";
}
</script>
<Popover.Root>
<Popover.Trigger>
{#snippet child({ props })}
<Button {...props} variant="outline" icon={CalendarDotsIcon}>{formatDate(date)}</Button>
{/snippet}
</Popover.Trigger>
<Popover.Content class="p-3">
<DatePicker mode="single" bind:value={date} />
</Popover.Content>
</Popover.Root>
Date Range with Popover
A date range picker in a popover with two months displayed.
<script lang="ts">
import type { DateValue } from "@internationalized/date";
import { getLocalTimeZone } from "@internationalized/date";
import { Button } from "kumo-svelte/components/button";
import { DatePicker } from "kumo-svelte/components/date-picker";
import * as Popover from "kumo-svelte/components/popover";
import CalendarDotsIcon from "phosphor-svelte/lib/CalendarDotsIcon";
type DateRangeValue = { start: DateValue | undefined; end: DateValue | undefined };
let range = $state<DateRangeValue | undefined>();
function formatRange(value: DateRangeValue | undefined) {
if (!value?.start) return "Select dates";
const start = value.start.toDate(getLocalTimeZone()).toLocaleDateString();
const end = value.end?.toDate(getLocalTimeZone()).toLocaleDateString();
return end ? `${start} – ${end}` : start;
}
</script>
<Popover.Root>
<Popover.Trigger>
{#snippet child({ props })}
<Button {...props} variant="outline" icon={CalendarDotsIcon}>{formatRange(range)}</Button>
{/snippet}
</Popover.Trigger>
<Popover.Content class="p-3">
<DatePicker mode="range" bind:value={range} numberOfMonths={2} />
</Popover.Content>
</Popover.Root>
Date Range with Presets
Combine the date picker with preset options for quick selection.
<script lang="ts">
import type { CalendarDate, DateValue } from "@internationalized/date";
import { getLocalTimeZone, today } from "@internationalized/date";
import { Button } from "kumo-svelte/components/button";
import { DatePicker } from "kumo-svelte/components/date-picker";
import * as Popover from "kumo-svelte/components/popover";
import CalendarDotsIcon from "phosphor-svelte/lib/CalendarDotsIcon";
type DateRangeValue = { start: DateValue | undefined; end: DateValue | undefined };
type PresetRange = { start: CalendarDate; end: CalendarDate };
const timeZone = getLocalTimeZone();
const now = today(timeZone);
const presets: Array<{ label: string; range: PresetRange }> = [
{ label: "Today", range: { start: now, end: now } },
{ label: "Last 7 days", range: { start: now.subtract({ days: 6 }), end: now } },
{ label: "Last 30 days", range: { start: now.subtract({ days: 29 }), end: now } },
{ label: "Last 90 days", range: { start: now.subtract({ days: 89 }), end: now } },
{ label: "This month", range: { start: now.set({ day: 1 }), end: now } },
{
label: "Last month",
range: {
start: now.subtract({ months: 1 }).set({ day: 1 }),
end: now.set({ day: 1 }).subtract({ days: 1 }),
},
},
];
let range = $state<DateRangeValue | undefined>();
let placeholder = $state(now);
function formatRange(value: DateRangeValue | undefined) {
if (!value?.start) return "Select dates";
const start = value.start.toDate(timeZone).toLocaleDateString();
const end = value.end?.toDate(timeZone).toLocaleDateString();
return end ? `${start} – ${end}` : start;
}
function selectPreset(preset: { range: PresetRange }) {
range = preset.range;
placeholder = preset.range.start;
}
function isPresetActive(preset: { range: PresetRange }) {
return range?.start?.compare(preset.range.start!) === 0 && range?.end?.compare(preset.range.end!) === 0;
}
</script>
<Popover.Root>
<Popover.Trigger>
{#snippet child({ props })}
<Button {...props} variant="outline" icon={CalendarDotsIcon}>{formatRange(range)}</Button>
{/snippet}
</Popover.Trigger>
<Popover.Content class="p-0">
<div class="flex">
<div class="flex flex-col gap-1 border-r border-kumo-hairline p-2 text-sm">
{#each presets as preset}
<button
type="button"
onclick={() => selectPreset(preset)}
class={[
"whitespace-nowrap rounded-md px-3 py-1.5 text-left",
isPresetActive(preset)
? "bg-kumo-bg-inverse text-kumo-text-inverse"
: "text-kumo-subtle hover:bg-kumo-control",
]}
>
{preset.label}
</button>
{/each}
</div>
<div class="p-3">
<DatePicker mode="range" bind:value={range} bind:placeholder numberOfMonths={2} />
</div>
</div>
</Popover.Content>
</Popover.Root>
Disabled Dates with Usage Limits
Use isDateDisabled or isDateUnavailable to make certain dates unselectable, and footer to display usage information.
| Su | Mo | Tu | We | Th | Fr | Sa |
|---|---|---|---|---|---|---|
<script lang="ts">
import type { DateValue } from "@internationalized/date";
import { getLocalTimeZone, today } from "@internationalized/date";
import { DatePicker } from "kumo-svelte/components/date-picker";
const timeZone = getLocalTimeZone();
const now = today(timeZone);
const maxDays = 5;
const unavailableDates = [
now.set({ day: 5 }),
now.set({ day: 12 }),
now.set({ day: 18 }),
now.set({ day: 25 }),
];
let dates = $state<DateValue[] | undefined>();
let selectedCount = $derived(dates?.length ?? 0);
function isUnavailable(value: DateValue) {
return unavailableDates.some((date) => date.compare(value) === 0);
}
</script>
{#snippet footer()}
<p class="w-full pt-2 text-xs text-kumo-subtle">
{selectedCount}/{maxDays} days selected. Grayed dates are unavailable.
</p>
{/snippet}
<DatePicker
mode="multiple"
bind:value={dates}
maxDays={maxDays}
isDateDisabled={isUnavailable}
fixedWeeks
{footer}
/>
Full Popover Example
Here’s a complete example showing how to compose DatePicker with Popover:
<script lang="ts">
import type { DateValue } from "@internationalized/date";
import { getLocalTimeZone } from "@internationalized/date";
import { Button } from "kumo-svelte/components/button";
import { DatePicker } from "kumo-svelte/components/date-picker";
import * as Popover from "kumo-svelte/components/popover";
import CalendarDotsIcon from "phosphor-svelte/lib/CalendarDotsIcon";
let date = $state<DateValue | undefined>();
function label(value: DateValue | undefined) {
return value ? value.toDate(getLocalTimeZone()).toLocaleDateString() : "Pick a date";
}
</script>
<Popover.Root>
<Popover.Trigger>
{#snippet child({ props })}
<Button {...props} variant="outline" icon={CalendarDotsIcon}>{label(date)}</Button>
{/snippet}
</Popover.Trigger>
<Popover.Content class="p-3">
<DatePicker mode="single" bind:value={date} />
</Popover.Content>
</Popover.Root> API Reference
DatePicker
| Prop | Type | Default |
|---|---|---|
| calendarLabel | string | — |
| children | Snippet<[DatePickerSnippetProps]> | — |
| class | string | — |
| disableDaysOutsideMonth | boolean | false |
| disabled | boolean | false |
| excludeDisabled | boolean | false |
| fixedWeeks | boolean | false |
| footer | Snippet | — |
| initialFocus | boolean | — |
| isDateDisabled | DateMatcher | — |
| isDateUnavailable | DateMatcher | — |
| locale | string | — |
| maxDays | number | — |
| maxValue | DateValue | — |
| minDays | number | — |
| minValue | DateValue | — |
| mode | DatePickerMode | "single" |
| monthFormat | Intl.DateTimeFormatOptions["month"] | ((month: number) => string) | — |
| numberOfMonths | number | 1 |
| onEndValueChange | (value: DateValue | undefined) => void | — |
| onPlaceholderChange | (value: DateValue) => void | — |
| onStartValueChange | (value: DateValue | undefined) => void | — |
| onValueChange | (value: DatePickerValue) => void | — |
| pagedNavigation | boolean | — |
| placeholder | DateValue | bindable |
| preventDeselect | boolean | — |
| readonly | boolean | false |
| ref | HTMLDivElement | null | null |
| value | DatePickerValue | bindable |
| weekStartsOn | WeekStartsOn | — |
| weekdayFormat | Intl.DateTimeFormatOptions["weekday"] | "short" |
| yearFormat | Intl.DateTimeFormatOptions["year"] | ((year: number) => string) | — |