Docs
Date Picker
Date Picker
A date picker component with range and presets.
Loading...
<script lang="ts">
import { Calendar as CalendarIcon } from "radix-icons-svelte";
import {
type DateValue,
DateFormatter,
getLocalTimeZone
} from "@internationalized/date";
import { cn } from "$lib/utils";
import { Button } from "$lib/components/ui/button";
import { Calendar } from "$lib/components/ui/calendar";
import * as Popover from "$lib/components/ui/popover";
const df = new DateFormatter("en-US", {
dateStyle: "long"
});
let value: DateValue | undefined = undefined;
</script>
<Popover.Root>
<Popover.Trigger asChild let:builder>
<Button
variant="outline"
class={cn(
"w-[240px] justify-start text-left font-normal",
!value && "text-muted-foreground"
)}
builders={[builder]}
>
<CalendarIcon class="mr-2 h-4 w-4" />
{value ? df.format(value.toDate(getLocalTimeZone())) : "Pick a date"}
</Button>
</Popover.Trigger>
<Popover.Content class="w-auto p-0" align="start">
<Calendar bind:value />
</Popover.Content>
</Popover.Root>
<script lang="ts">
import { Calendar as CalendarIcon } from "lucide-svelte";
import {
type DateValue,
DateFormatter,
getLocalTimeZone
} from "@internationalized/date";
import { cn } from "$lib/utils";
import { Button } from "$lib/components/ui/button";
import { Calendar } from "$lib/components/ui/calendar";
import * as Popover from "$lib/components/ui/popover";
const df = new DateFormatter("en-US", {
dateStyle: "long"
});
let value: DateValue | undefined = undefined;
</script>
<Popover.Root>
<Popover.Trigger asChild let:builder>
<Button
variant="outline"
class={cn(
"w-[280px] justify-start text-left font-normal",
!value && "text-muted-foreground"
)}
builders={[builder]}
>
<CalendarIcon class="mr-2 h-4 w-4" />
{value ? df.format(value.toDate(getLocalTimeZone())) : "Pick a date"}
</Button>
</Popover.Trigger>
<Popover.Content class="w-auto p-0">
<Calendar bind:value initialFocus />
</Popover.Content>
</Popover.Root>
Installation
The Date Picker is built using a composition of the <Popover />
and either the <Calendar />
or <RangeCalendar />
components.
See installations instructions for the Popover, Calendar, and Range Calendar components.
Usage
<script lang="ts">
import { Calendar as CalendarIcon } from "lucide-svelte";
import {
type DateValue,
DateFormatter,
getLocalTimeZone,
} from "@internationalized/date";
import { cn } from "$lib/utils";
import { Button } from "$lib/components/ui/button";
import { Calendar } from "$lib/components/ui/calendar";
import * as Popover from "$lib/components/ui/popover";
const df = new DateFormatter("en-US", {
dateStyle: "long",
});
let value: DateValue | undefined = undefined;
</script>
<Popover.Root openFocus>
<Popover.Trigger asChild let:builder>
<Button
variant="outline"
class={cn(
"w-[280px] justify-start text-left font-normal",
!value && "text-muted-foreground"
)}
builders={[builder]}
>
<CalendarIcon class="mr-2 h-4 w-4" />
{value ? df.format(value.toDate(getLocalTimeZone())) : "Select a date"}
</Button>
</Popover.Trigger>
<Popover.Content class="w-auto p-0">
<Calendar bind:value initialFocus />
</Popover.Content>
</Popover.Root>
Examples
Date Picker
Loading...
<script lang="ts">
import { Calendar as CalendarIcon } from "radix-icons-svelte";
import {
type DateValue,
DateFormatter,
getLocalTimeZone
} from "@internationalized/date";
import { cn } from "$lib/utils";
import { Button } from "$lib/components/ui/button";
import { Calendar } from "$lib/components/ui/calendar";
import * as Popover from "$lib/components/ui/popover";
const df = new DateFormatter("en-US", {
dateStyle: "long"
});
let value: DateValue | undefined = undefined;
</script>
<Popover.Root>
<Popover.Trigger asChild let:builder>
<Button
variant="outline"
class={cn(
"w-[240px] justify-start text-left font-normal",
!value && "text-muted-foreground"
)}
builders={[builder]}
>
<CalendarIcon class="mr-2 h-4 w-4" />
{value ? df.format(value.toDate(getLocalTimeZone())) : "Pick a date"}
</Button>
</Popover.Trigger>
<Popover.Content class="w-auto p-0" align="start">
<Calendar bind:value />
</Popover.Content>
</Popover.Root>
<script lang="ts">
import { Calendar as CalendarIcon } from "lucide-svelte";
import {
type DateValue,
DateFormatter,
getLocalTimeZone
} from "@internationalized/date";
import { cn } from "$lib/utils";
import { Button } from "$lib/components/ui/button";
import { Calendar } from "$lib/components/ui/calendar";
import * as Popover from "$lib/components/ui/popover";
const df = new DateFormatter("en-US", {
dateStyle: "long"
});
let value: DateValue | undefined = undefined;
</script>
<Popover.Root>
<Popover.Trigger asChild let:builder>
<Button
variant="outline"
class={cn(
"w-[280px] justify-start text-left font-normal",
!value && "text-muted-foreground"
)}
builders={[builder]}
>
<CalendarIcon class="mr-2 h-4 w-4" />
{value ? df.format(value.toDate(getLocalTimeZone())) : "Pick a date"}
</Button>
</Popover.Trigger>
<Popover.Content class="w-auto p-0">
<Calendar bind:value initialFocus />
</Popover.Content>
</Popover.Root>
Date Range Picker
Loading...
<script lang="ts">
import { Calendar as CalendarIcon } from "radix-icons-svelte";
import type { DateRange } from "bits-ui";
import {
CalendarDate,
DateFormatter,
getLocalTimeZone,
type DateValue
} from "@internationalized/date";
import { cn } from "$lib/utils";
import { Button } from "$lib/components/ui/button";
import { RangeCalendar } from "$lib/components/ui/range-calendar";
import * as Popover from "$lib/components/ui/popover";
const df = new DateFormatter("en-US", {
dateStyle: "medium"
});
let value: DateRange | undefined = {
start: new CalendarDate(2022, 1, 20),
end: new CalendarDate(2022, 1, 20).add({ days: 20 })
};
let startValue: DateValue | undefined = undefined;
</script>
<div class="grid gap-2">
<Popover.Root openFocus>
<Popover.Trigger asChild let:builder>
<Button
variant="outline"
class={cn(
"w-[300px] justify-start text-left font-normal",
!value && "text-muted-foreground"
)}
builders={[builder]}
>
<CalendarIcon class="mr-2 h-4 w-4" />
{#if value && value.start}
{#if value.end}
{df.format(value.start.toDate(getLocalTimeZone()))} - {df.format(
value.end.toDate(getLocalTimeZone())
)}
{:else}
{df.format(value.start.toDate(getLocalTimeZone()))}
{/if}
{:else if startValue}
{df.format(startValue.toDate(getLocalTimeZone()))}
{:else}
Pick a date
{/if}
</Button>
</Popover.Trigger>
<Popover.Content class="w-auto p-0" align="start">
<RangeCalendar
bind:value
bind:startValue
placeholder={value?.start}
initialFocus
numberOfMonths={2}
/>
</Popover.Content>
</Popover.Root>
</div>
<script lang="ts">
import { Calendar as CalendarIcon } from "lucide-svelte";
import type { DateRange } from "bits-ui";
import {
CalendarDate,
DateFormatter,
getLocalTimeZone,
type DateValue
} from "@internationalized/date";
import { cn } from "$lib/utils";
import { Button } from "$lib/components/ui/button";
import { RangeCalendar } from "$lib/components/ui/range-calendar";
import * as Popover from "$lib/components/ui/popover";
const df = new DateFormatter("en-US", {
dateStyle: "medium"
});
let value: DateRange | undefined = {
start: new CalendarDate(2022, 1, 20),
end: new CalendarDate(2022, 1, 20).add({ days: 20 })
};
let startValue: DateValue | undefined = undefined;
</script>
<div class="grid gap-2">
<Popover.Root openFocus>
<Popover.Trigger asChild let:builder>
<Button
variant="outline"
class={cn(
"w-[300px] justify-start text-left font-normal",
!value && "text-muted-foreground"
)}
builders={[builder]}
>
<CalendarIcon class="mr-2 h-4 w-4" />
{#if value && value.start}
{#if value.end}
{df.format(value.start.toDate(getLocalTimeZone()))} - {df.format(
value.end.toDate(getLocalTimeZone())
)}
{:else}
{df.format(value.start.toDate(getLocalTimeZone()))}
{/if}
{:else if startValue}
{df.format(startValue.toDate(getLocalTimeZone()))}
{:else}
Pick a date
{/if}
</Button>
</Popover.Trigger>
<Popover.Content class="w-auto p-0" align="start">
<RangeCalendar
bind:value
bind:startValue
initialFocus
numberOfMonths={2}
placeholder={value?.start}
/>
</Popover.Content>
</Popover.Root>
</div>
With Presets
Loading...
<script lang="ts">
import { Calendar as CalendarIcon } from "radix-icons-svelte";
import {
type DateValue,
DateFormatter,
getLocalTimeZone,
today
} from "@internationalized/date";
import { cn } from "$lib/utils";
import { Button } from "$lib/components/ui/button";
import { Calendar } from "$lib/components/ui/calendar";
import * as Popover from "$lib/components/ui/popover";
import * as Select from "$lib/components/ui/select";
const df = new DateFormatter("en-US", {
dateStyle: "long"
});
let value: DateValue | undefined = undefined;
const items = [
{ value: 0, label: "Today" },
{ value: 1, label: "Tomorrow" },
{ value: 3, label: "In 3 days" },
{ value: 7, label: "In a week" }
];
</script>
<Popover.Root openFocus>
<Popover.Trigger asChild let:builder>
<Button
variant="outline"
class={cn(
"w-[240px] justify-start text-left font-normal",
!value && "text-muted-foreground"
)}
builders={[builder]}
>
<CalendarIcon class="mr-2 h-4 w-4" />
{value ? df.format(value.toDate(getLocalTimeZone())) : "Pick a date"}
</Button>
</Popover.Trigger>
<Popover.Content class="flex w-auto flex-col space-y-2 p-2">
<Select.Root
{items}
onSelectedChange={(v) => {
if (!v) return;
value = today(getLocalTimeZone()).add({ days: v.value });
}}
>
<Select.Trigger>
<Select.Value placeholder="Select" />
</Select.Trigger>
<Select.Content>
{#each items as item}
<Select.Item value={item.value}>{item.label}</Select.Item>
{/each}
</Select.Content>
</Select.Root>
<div class="rounded-md border">
<Calendar bind:value />
</div>
</Popover.Content>
</Popover.Root>
<script lang="ts">
import { Calendar as CalendarIcon } from "lucide-svelte";
import {
type DateValue,
DateFormatter,
getLocalTimeZone,
today
} from "@internationalized/date";
import { cn } from "$lib/utils";
import { Button } from "$lib/components/ui/button";
import { Calendar } from "$lib/components/ui/calendar";
import * as Popover from "$lib/components/ui/popover";
import * as Select from "$lib/components/ui/select";
const df = new DateFormatter("en-US", {
dateStyle: "long"
});
let value: DateValue | undefined = undefined;
const items = [
{ value: 0, label: "Today" },
{ value: 1, label: "Tomorrow" },
{ value: 3, label: "In 3 days" },
{ value: 7, label: "In a week" }
];
</script>
<Popover.Root openFocus>
<Popover.Trigger asChild let:builder>
<Button
variant="outline"
class={cn(
"w-[280px] justify-start text-left font-normal",
!value && "text-muted-foreground"
)}
builders={[builder]}
>
<CalendarIcon class="mr-2 h-4 w-4" />
{value ? df.format(value.toDate(getLocalTimeZone())) : "Pick a date"}
</Button>
</Popover.Trigger>
<Popover.Content class="flex w-auto flex-col space-y-2 p-2">
<Select.Root
{items}
onSelectedChange={(v) => {
if (!v) return;
value = today(getLocalTimeZone()).add({ days: v.value });
}}
>
<Select.Trigger>
<Select.Value placeholder="Select" />
</Select.Trigger>
<Select.Content>
{#each items as item}
<Select.Item value={item.value}>{item.label}</Select.Item>
{/each}
</Select.Content>
</Select.Root>
<div class="rounded-md border">
<Calendar bind:value />
</div>
</Popover.Content>
</Popover.Root>
Form
Loading...
<script lang="ts" context="module">
import { z } from "zod";
export const formSchema = z.object({
dob: z
.string()
.refine((v) => v, { message: "A date of birth is required." })
});
export type FormSchema = typeof formSchema;
</script>
<script lang="ts">
import { page } from "$app/stores";
import { Calendar as CalendarIcon } from "radix-icons-svelte";
import {
type DateValue,
DateFormatter,
getLocalTimeZone,
parseDate,
CalendarDate,
today
} from "@internationalized/date";
import { cn } from "$lib/utils";
import { Button, buttonVariants } from "$lib/components/ui/button";
import { Calendar } from "$lib/components/ui/calendar";
import * as Popover from "$lib/components/ui/popover";
import * as Form from "$lib/components/ui/form";
import type { SuperValidated } from "sveltekit-superforms";
import { superForm } from "sveltekit-superforms/client";
export let form: SuperValidated<FormSchema> = $page.data.datePicker;
const theForm = superForm(form, {
validators: formSchema,
taintedMessage: null
});
const { form: formStore } = theForm;
const df = new DateFormatter("en-US", {
dateStyle: "long"
});
let value: DateValue | undefined = $formStore.dob
? parseDate($formStore.dob)
: undefined;
let placeholder: DateValue = today(getLocalTimeZone());
</script>
<Form.Root
schema={formSchema}
controlled
form={theForm}
let:config
action="?/datePicker"
class="space-y-8"
>
<Form.Field {config} name="dob">
<Form.Item class="flex flex-col">
<Form.Label for="dob">Date of birth</Form.Label>
<Popover.Root>
<Form.Control id="dob" let:attrs>
<Popover.Trigger
id="dob"
{...attrs}
class={cn(
buttonVariants({ variant: "outline" }),
"w-[240px] pl-3 justify-start text-left font-normal",
!value && "text-muted-foreground"
)}
>
{value
? df.format(value.toDate(getLocalTimeZone()))
: "Pick a date"}
<CalendarIcon class="ml-auto opacity-50 h-4 w-4" />
</Popover.Trigger>
</Form.Control>
<Popover.Content class="w-auto p-0" side="top">
<Calendar
bind:value
bind:placeholder
minValue={new CalendarDate(1900, 1, 1)}
maxValue={today(getLocalTimeZone())}
calendarLabel="Date of birth"
initialFocus
onValueChange={(v) => {
if (v) {
$formStore.dob = v.toString();
} else {
$formStore.dob = "";
}
}}
/>
</Popover.Content>
</Popover.Root>
<Form.Description
>Your date of birth is used to calculator your age</Form.Description
>
<Form.Validation />
</Form.Item>
</Form.Field>
<Button type="submit">Submit</Button>
</Form.Root>
<script lang="ts" context="module">
import { z } from "zod";
export const formSchema = z.object({
dob: z
.string()
.refine((v) => v, { message: "A date of birth is required." })
});
export type FormSchema = typeof formSchema;
</script>
<script lang="ts">
import { page } from "$app/stores";
import { Calendar as CalendarIcon } from "lucide-svelte";
import {
type DateValue,
DateFormatter,
getLocalTimeZone,
parseDate,
CalendarDate,
today
} from "@internationalized/date";
import { cn } from "$lib/utils";
import { Button, buttonVariants } from "$lib/components/ui/button";
import { Calendar } from "$lib/components/ui/calendar";
import * as Popover from "$lib/components/ui/popover";
import * as Form from "$lib/components/ui/form";
import type { SuperValidated } from "sveltekit-superforms";
import { superForm } from "sveltekit-superforms/client";
export let form: SuperValidated<FormSchema> = $page.data.datePicker;
const theForm = superForm(form, {
validators: formSchema,
taintedMessage: null
});
const { form: formStore } = theForm;
const df = new DateFormatter("en-US", {
dateStyle: "long"
});
let value: DateValue | undefined = $formStore.dob
? parseDate($formStore.dob)
: undefined;
let placeholder: DateValue = today(getLocalTimeZone());
</script>
<Form.Root
schema={formSchema}
controlled
form={theForm}
let:config
action="?/datePicker"
class="space-y-8"
>
<Form.Field {config} name="dob">
<Form.Item class="flex flex-col">
<Form.Label for="dob">Date of birth</Form.Label>
<Popover.Root>
<Form.Control id="dob" let:attrs>
<Popover.Trigger
id="dob"
{...attrs}
class={cn(
buttonVariants({ variant: "outline" }),
"w-[280px] pl-4 justify-start text-left font-normal",
!value && "text-muted-foreground"
)}
>
{value
? df.format(value.toDate(getLocalTimeZone()))
: "Pick a date"}
<CalendarIcon class="ml-auto h-4 w-4 opacity-50" />
</Popover.Trigger>
</Form.Control>
<Popover.Content class="w-auto p-0" side="top">
<Calendar
bind:value
bind:placeholder
minValue={new CalendarDate(1900, 1, 1)}
maxValue={today(getLocalTimeZone())}
calendarLabel="Date of birth"
initialFocus
onValueChange={(v) => {
if (v) {
$formStore.dob = v.toString();
} else {
$formStore.dob = "";
}
}}
/>
</Popover.Content>
</Popover.Root>
<Form.Description
>Your date of birth is used to calculator your age</Form.Description
>
<Form.Validation />
</Form.Item>
</Form.Field>
<Button type="submit">Submit</Button>
</Form.Root>
On This Page