Docs
   Calendar
Calendar
A calendar component that allows users to select dates.
								Loading...
  	<script lang="ts">
  import { Calendar } from "$lib/components/ui/calendar/index.js";
  import { today, getLocalTimeZone } from "@internationalized/date";
 
  let value = today(getLocalTimeZone());
</script>
 
<Calendar bind:value class="rounded-md border shadow" />
 	<script lang="ts">
  import { Calendar } from "$lib/components/ui/calendar/index.js";
  import { today, getLocalTimeZone } from "@internationalized/date";
 
  let value = today(getLocalTimeZone());
</script>
 
<Calendar bind:value class="rounded-md border" />
 About
The <Calendar /> component is built on top of the Bits Calendar component, which uses the @internationalized/date package to handle dates.
If you're looking for a range calendar, check out the Range Calendar component.
Installation
	npx shadcn-svelte@latest add calendar
 Date Picker
You can use the <Calendar /> component to build a date picker. See the Date Picker page for more information.
Examples
Form
								Loading...
  	<script lang="ts">
  import CalendarIcon from "svelte-radix/Calendar.svelte";
  import {
    type DateValue,
    DateFormatter,
    getLocalTimeZone
  } from "@internationalized/date";
  import { cn } from "$lib/utils.js";
  import { Button } from "$lib/components/ui/button/index.js";
  import { Calendar } from "$lib/components/ui/calendar/index.js";
  import * as Popover from "$lib/components/ui/popover/index.js";
 
  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 CalendarIcon from "lucide-svelte/icons/calendar";
  import {
    type DateValue,
    DateFormatter,
    getLocalTimeZone
  } from "@internationalized/date";
  import { cn } from "$lib/utils.js";
  import { Button } from "$lib/components/ui/button/index.js";
  import { Calendar } from "$lib/components/ui/calendar/index.js";
  import * as Popover from "$lib/components/ui/popover/index.js";
 
  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>
 Advanced Customization
The <Calendar /> component can be combined with other components to create a more complex calendar.
By default, we export the combined Calendar component as 
Calendar as there are quite a few pieces that need to be combined to create it. We're modifying that component in the examples below.Month & Year Selects
Here's an example of how you could create a calendar with month and year select dropdowns instead of the previous and next buttons.
								Loading...
  	<script lang="ts">
  import { Calendar as CalendarPrimitive } from "bits-ui";
  import * as Calendar from "$lib/components/ui/calendar/index.js";
  import * as Select from "$lib/components/ui/select/index.js";
  import { cn } from "$lib/utils.js";
  import {
    DateFormatter,
    getLocalTimeZone,
    today
  } from "@internationalized/date";
 
  type $$Props = CalendarPrimitive.Props;
  type $$Events = CalendarPrimitive.Events;
 
  export let value: $$Props["value"] = undefined;
  export let placeholder: $$Props["placeholder"] = today(getLocalTimeZone());
  export let weekdayFormat: $$Props["weekdayFormat"] = "short";
 
  const monthOptions = [
    "January",
    "February",
    "March",
    "April",
    "May",
    "June",
    "July",
    "August",
    "September",
    "October",
    "November",
    "December"
  ].map((month, i) => ({ value: i + 1, label: month }));
 
  const monthFmt = new DateFormatter("en-US", {
    month: "long"
  });
 
  const yearOptions = Array.from({ length: 100 }, (_, i) => ({
    label: String(new Date().getFullYear() - i),
    value: new Date().getFullYear() - i
  }));
 
  $: defaultYear = placeholder
    ? {
        value: placeholder.year,
        label: String(placeholder.year)
      }
    : undefined;
 
  $: defaultMonth = placeholder
    ? {
        value: placeholder.month,
        label: monthFmt.format(placeholder.toDate(getLocalTimeZone()))
      }
    : undefined;
 
  let className: $$Props["class"] = undefined;
  export { className as class };
</script>
 
<CalendarPrimitive.Root
  bind:value
  bind:placeholder
  {weekdayFormat}
  class={cn("rounded-md border p-3", className)}
  {...$$restProps}
  on:keydown
  let:months
  let:weekdays
>
  <Calendar.Header>
    <Calendar.Heading class="flex w-full items-center justify-between gap-2">
      <Select.Root
        selected={defaultMonth}
        items={monthOptions}
        onSelectedChange={(v) => {
          if (!v || !placeholder) return;
          if (v.value === placeholder?.month) return;
          placeholder = placeholder.set({ month: v.value });
        }}
      >
        <Select.Trigger aria-label="Select month" class="w-[60%]">
          <Select.Value placeholder="Select month" />
        </Select.Trigger>
        <Select.Content class="max-h-[200px] overflow-y-auto">
          {#each monthOptions as { value, label }}
            <Select.Item {value} {label}>
              {label}
            </Select.Item>
          {/each}
        </Select.Content>
      </Select.Root>
      <Select.Root
        selected={defaultYear}
        items={yearOptions}
        onSelectedChange={(v) => {
          if (!v || !placeholder) return;
          if (v.value === placeholder?.year) return;
          placeholder = placeholder.set({ year: v.value });
        }}
      >
        <Select.Trigger aria-label="Select year" class="w-[40%]">
          <Select.Value placeholder="Select year" />
        </Select.Trigger>
        <Select.Content class="max-h-[200px] overflow-y-auto">
          {#each yearOptions as { value, label }}
            <Select.Item {value} {label}>
              {label}
            </Select.Item>
          {/each}
        </Select.Content>
      </Select.Root>
    </Calendar.Heading>
  </Calendar.Header>
  <Calendar.Months>
    {#each months as month}
      <Calendar.Grid>
        <Calendar.GridHead>
          <Calendar.GridRow class="flex">
            {#each weekdays as weekday}
              <Calendar.HeadCell>
                {weekday.slice(0, 2)}
              </Calendar.HeadCell>
            {/each}
          </Calendar.GridRow>
        </Calendar.GridHead>
        <Calendar.GridBody>
          {#each month.weeks as weekDates}
            <Calendar.GridRow class="mt-2 w-full">
              {#each weekDates as date}
                <Calendar.Cell {date}>
                  <Calendar.Day {date} month={month.value} />
                </Calendar.Cell>
              {/each}
            </Calendar.GridRow>
          {/each}
        </Calendar.GridBody>
      </Calendar.Grid>
    {/each}
  </Calendar.Months>
</CalendarPrimitive.Root>
 	<script lang="ts">
  import { Calendar as CalendarPrimitive } from "bits-ui";
  import * as Calendar from "$lib/components/ui/calendar/index.js";
  import * as Select from "$lib/components/ui/select/index.js";
  import { cn } from "$lib/utils.js";
  import {
    DateFormatter,
    getLocalTimeZone,
    today
  } from "@internationalized/date";
 
  type $$Props = CalendarPrimitive.Props;
  type $$Events = CalendarPrimitive.Events;
 
  export let value: $$Props["value"] = undefined;
  export let placeholder: $$Props["placeholder"] = today(getLocalTimeZone());
  export let weekdayFormat: $$Props["weekdayFormat"] = "short";
 
  const monthOptions = [
    "January",
    "February",
    "March",
    "April",
    "May",
    "June",
    "July",
    "August",
    "September",
    "October",
    "November",
    "December"
  ].map((month, i) => ({ value: i + 1, label: month }));
 
  const monthFmt = new DateFormatter("en-US", {
    month: "long"
  });
 
  const yearOptions = Array.from({ length: 100 }, (_, i) => ({
    label: String(new Date().getFullYear() - i),
    value: new Date().getFullYear() - i
  }));
 
  $: defaultYear = placeholder
    ? {
        value: placeholder.year,
        label: String(placeholder.year)
      }
    : undefined;
 
  $: defaultMonth = placeholder
    ? {
        value: placeholder.month,
        label: monthFmt.format(placeholder.toDate(getLocalTimeZone()))
      }
    : undefined;
 
  let className: $$Props["class"] = undefined;
  export { className as class };
</script>
 
<CalendarPrimitive.Root
  {weekdayFormat}
  class={cn("rounded-md border p-3", className)}
  {...$$restProps}
  on:keydown
  let:months
  let:weekdays
  bind:value
  bind:placeholder
>
  <Calendar.Header>
    <Calendar.Heading class="flex w-full items-center justify-between gap-2">
      <Select.Root
        selected={defaultMonth}
        items={monthOptions}
        onSelectedChange={(v) => {
          if (!v || !placeholder) return;
          if (v.value === placeholder?.month) return;
          placeholder = placeholder.set({ month: v.value });
        }}
      >
        <Select.Trigger aria-label="Select month" class="w-[60%]">
          <Select.Value placeholder="Select month" />
        </Select.Trigger>
        <Select.Content class="max-h-[200px] overflow-y-auto">
          {#each monthOptions as { value, label }}
            <Select.Item {value} {label}>
              {label}
            </Select.Item>
          {/each}
        </Select.Content>
      </Select.Root>
      <Select.Root
        selected={defaultYear}
        items={yearOptions}
        onSelectedChange={(v) => {
          if (!v || !placeholder) return;
          if (v.value === placeholder?.year) return;
          placeholder = placeholder.set({ year: v.value });
        }}
      >
        <Select.Trigger aria-label="Select year" class="w-[40%]">
          <Select.Value placeholder="Select year" />
        </Select.Trigger>
        <Select.Content class="max-h-[200px] overflow-y-auto">
          {#each yearOptions as { value, label }}
            <Select.Item {value} {label}>
              {label}
            </Select.Item>
          {/each}
        </Select.Content>
      </Select.Root>
    </Calendar.Heading>
  </Calendar.Header>
  <Calendar.Months>
    {#each months as month}
      <Calendar.Grid>
        <Calendar.GridHead>
          <Calendar.GridRow class="flex">
            {#each weekdays as weekday}
              <Calendar.HeadCell>
                {weekday.slice(0, 2)}
              </Calendar.HeadCell>
            {/each}
          </Calendar.GridRow>
        </Calendar.GridHead>
        <Calendar.GridBody>
          {#each month.weeks as weekDates}
            <Calendar.GridRow class="mt-2 w-full">
              {#each weekDates as date}
                <Calendar.Cell {date}>
                  <Calendar.Day {date} month={month.value} />
                </Calendar.Cell>
              {/each}
            </Calendar.GridRow>
          {/each}
        </Calendar.GridBody>
      </Calendar.Grid>
    {/each}
  </Calendar.Months>
</CalendarPrimitive.Root>
 On This Page