Docs
  Calendar
Calendar
A calendar component that allows users to select dates.
	<script lang="ts">
  import { getLocalTimeZone, today } from "@internationalized/date";
  import { Calendar } from "$lib/components/ui/calendar/index.js";
 
  let value = $state(today(getLocalTimeZone()));
</script>
 
<Calendar type="single" bind:value class="rounded-md border shadow" />
  
	<script lang="ts">
  import { getLocalTimeZone, today } from "@internationalized/date";
  import { Calendar } from "$lib/components/ui/calendar/index.js";
 
  let value = today(getLocalTimeZone());
</script>
 
<Calendar type="single" 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@next add calendar
   Install bits-ui and @internationalized/date:
	npm i bits-ui @internationalized/date -D
   Copy and paste the component source files linked at the top of this page into your project.
Date Picker
You can use the <Calendar /> component to build a date picker. See the Date Picker page for more information.
Examples
Form
	<script lang="ts">
  import CalendarIcon from "lucide-svelte/icons/calendar";
  import {
    DateFormatter,
    type DateValue,
    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>
    {#snippet child({ props })}
      <Button
        variant="outline"
        class={cn(
          "w-[240px] justify-start text-left font-normal",
          !value && "text-muted-foreground"
        )}
        {...props}
      >
        <CalendarIcon />
        {value ? df.format(value.toDate(getLocalTimeZone())) : "Pick a date"}
      </Button>
    {/snippet}
  </Popover.Trigger>
  <Popover.Content class="w-auto p-0" align="start">
    <Calendar type="single" bind:value />
  </Popover.Content>
</Popover.Root>
  
	<script lang="ts">
  import CalendarIcon from "lucide-svelte/icons/calendar";
  import {
    DateFormatter,
    type DateValue,
    getLocalTimeZone
  } from "@internationalized/date";
  import { cn } from "$lib/utils.js";
  import { buttonVariants } 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 = $state<DateValue | undefined>();
  let contentRef = $state<HTMLElement | null>(null);
</script>
 
<Popover.Root>
  <Popover.Trigger
    class={cn(
      buttonVariants({
        variant: "outline",
        class: "w-[280px] justify-start text-left font-normal"
      }),
      !value && "text-muted-foreground"
    )}
  >
    <CalendarIcon />
    {value ? df.format(value.toDate(getLocalTimeZone())) : "Pick a date"}
  </Popover.Trigger>
  <Popover.Content bind:ref={contentRef} class="w-auto p-0">
    <Calendar type="single" bind:value />
  </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.
	<script lang="ts">
  import {
    Calendar as CalendarPrimitive,
    type WithoutChildrenOrChild
  } from "bits-ui";
  import { DateFormatter, getLocalTimeZone } from "@internationalized/date";
  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";
 
  let {
    value = $bindable(),
    placeholder = $bindable(),
    weekdayFormat = "short",
    class: className,
    ...restProps
  }: WithoutChildrenOrChild<CalendarPrimitive.RootProps> = $props();
 
  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
  }));
 
  const defaultYear = $derived(
    placeholder
      ? { value: placeholder.year, label: String(placeholder.year) }
      : undefined
  );
 
  const defaultMonth = $derived(
    placeholder
      ? {
          value: placeholder.month,
          label: monthFmt.format(placeholder.toDate(getLocalTimeZone()))
        }
      : undefined
  );
 
  const monthLabel = $derived(
    monthOptions.find((m) => m.value === defaultMonth?.value)?.label ??
      "Select a month"
  );
</script>
 
<CalendarPrimitive.Root
  bind:value={value as never}
  bind:placeholder
  {weekdayFormat}
  class={cn("rounded-md border p-3", className)}
  {...restProps}
>
  {#snippet children({ months, weekdays })}
    <Calendar.Header>
      <Calendar.Heading class="flex w-full items-center justify-between gap-2">
        <Select.Root
          type="single"
          value={`${defaultMonth?.value}`}
          onValueChange={(v) => {
            if (!v || !placeholder) return;
            if (v === `${placeholder?.month}`) return;
            placeholder = placeholder.set({ month: Number.parseInt(v) });
          }}
        >
          <Select.Trigger aria-label="Select month" class="w-[60%]">
            {monthLabel}
          </Select.Trigger>
          <Select.Content class="max-h-[200px] overflow-y-auto">
            {#each monthOptions as { value, label }}
              <Select.Item value={`${value}`} {label} />
            {/each}
          </Select.Content>
        </Select.Root>
        <Select.Root
          type="single"
          value={`${defaultYear?.value}`}
          onValueChange={(v) => {
            if (!v || !placeholder) return;
            if (v === `${placeholder?.year}`) return;
            placeholder = placeholder.set({ year: Number.parseInt(v) });
          }}
        >
          <Select.Trigger aria-label="Select year" class="w-[40%]">
            {defaultYear?.label ?? "Select year"}
          </Select.Trigger>
          <Select.Content class="max-h-[200px] overflow-y-auto">
            {#each yearOptions as { value, label }}
              <Select.Item value={`${value}`} {label} />
            {/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} month={month.value}>
                    <Calendar.Day />
                  </Calendar.Cell>
                {/each}
              </Calendar.GridRow>
            {/each}
          </Calendar.GridBody>
        </Calendar.Grid>
      {/each}
    </Calendar.Months>
  {/snippet}
</CalendarPrimitive.Root>
  
	<script lang="ts">
  import {
    Calendar as CalendarPrimitive,
    type CalendarRootProps,
    type WithoutChildrenOrChild
  } from "bits-ui";
  import { DateFormatter, getLocalTimeZone } from "@internationalized/date";
  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";
 
  let {
    value = $bindable(),
    placeholder = $bindable(),
    weekdayFormat,
    class: className,
    ...restProps
  }: WithoutChildrenOrChild<CalendarRootProps> = $props();
 
  const monthOptions = [
    "January",
    "February",
    "March",
    "April",
    "May",
    "June",
    "July",
    "August",
    "September",
    "October",
    "November",
    "December"
  ].map((month, i) => ({ value: String(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: String(new Date().getFullYear() - i)
  }));
 
  const defaultYear = $derived(
    placeholder
      ? { value: String(placeholder.year), label: String(placeholder.year) }
      : undefined
  );
 
  const defaultMonth = $derived(
    placeholder
      ? {
          value: String(placeholder.month),
          label: monthFmt.format(placeholder.toDate(getLocalTimeZone()))
        }
      : undefined
  );
 
  const monthLabel = $derived(
    monthOptions.find((m) => m.value === defaultMonth?.value)?.label ??
      "Select a month"
  );
</script>
 
<CalendarPrimitive.Root
  {weekdayFormat}
  class={cn("rounded-md border p-3", className)}
  bind:value={value as never}
  bind:placeholder
  {...restProps}
>
  {#snippet children({ months, weekdays })}
    <Calendar.Header>
      <Calendar.Heading class="flex w-full items-center justify-between gap-2">
        <Select.Root
          type="single"
          value={defaultMonth?.value}
          onValueChange={(v) => {
            if (!placeholder) return;
            if (v === `${placeholder.month}`) return;
            placeholder = placeholder.set({ month: Number.parseInt(v) });
          }}
        >
          <Select.Trigger aria-label="Select month" class="w-[60%]">
            {monthLabel}
          </Select.Trigger>
          <Select.Content class="max-h-[200px] overflow-y-auto">
            {#each monthOptions as { value, label }}
              <Select.Item {value} {label} />
            {/each}
          </Select.Content>
        </Select.Root>
        <Select.Root
          type="single"
          value={defaultYear?.value}
          onValueChange={(v) => {
            if (!v || !placeholder) return;
            if (v === `${placeholder?.year}`) return;
            placeholder = placeholder.set({ year: Number.parseInt(v) });
          }}
        >
          <Select.Trigger aria-label="Select year" class="w-[40%]">
            {defaultYear?.label ?? "Select year"}
          </Select.Trigger>
          <Select.Content class="max-h-[200px] overflow-y-auto">
            {#each yearOptions as { value, label }}
              <Select.Item {value} {label} />
            {/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} month={month.value}>
                    <Calendar.Day />
                  </Calendar.Cell>
                {/each}
              </Calendar.GridRow>
            {/each}
          </Calendar.GridBody>
        </Calendar.Grid>
      {/each}
    </Calendar.Months>
  {/snippet}
</CalendarPrimitive.Root>
  On This Page