Presets

Blocks

Get PRO

Need premium blocks and templates? Upgrade to PRO and get access.

Upgrade
JoinLogin
Presets
Monochrome
Overview
  • Introduction
  • Components
  • Installation
Components
  • Accordion
  • Alert
  • Alert Dialog
  • Aspect Ratio
  • Avatar
  • Badge
  • Breadcrumb
  • Button
  • Button Group
  • CalendarNEW
  • Card
  • CarouselNEW
  • Checkbox
  • Collapsible
  • Dialog
  • Dropdown Menu
  • Empty
  • Field
  • Input
  • Input Group
  • Item
  • Kbd
  • Label
  • Native Select
  • Scroll Area
  • SelectNEW
  • Separator
  • SwitchNEW
  • Table
  • Tabs
  • Textarea
  • TooltipNEW

Calendar

PreviousNext

A calendar component that allows users to select a date or a range of dates.

1<script module lang="ts">
2  import { Calendar } from "@/components/ui/calendar"
3  import { CalendarDate, type DateValue } from "@internationalized/date"
4  import { type DatePickerValueChangeDetails } from "@sprawlify/svelte/date-picker"

Installation

pnpm dlx sprawlify@latest add calendar
npx sprawlify@latest add calendar
yarn sprawlify@latest add calendar
bunx --bun sprawlify@latest add calendar

Install the following dependencies:

pnpm add @sprawlify/primitives @sprawlify/svelte
npm install @sprawlify/primitives @sprawlify/svelte
yarn add @sprawlify/primitives @sprawlify/svelte
bun add @sprawlify/primitives @sprawlify/svelte

Add the following files to your project:

calendar/calendar-day-button.svelte
1<script lang="ts">
2  import { DatePicker as DatePickerPrimitive } from "@sprawlify/svelte/date-picker"
3  import { cn } from "@/lib/utils"
4  import type { ComponentProps, Snippet } from "svelte"
5  
6  interface CalendarDayButtonProps extends ComponentProps<typeof DatePickerPrimitive.TableCellTrigger> {}
7
8  let { 
9    children, 
10    class: className, 
11    ...props 
12  }: CalendarDayButtonProps = $props()
13</script>
14
15<DatePickerPrimitive.TableCellTrigger
16  class={cn(
17    "relative isolate z-10 flex aspect-square size-auto w-full min-w-(--cell-size) items-center justify-center gap-1 rounded-(--cell-radius) border-0 text-sm leading-none font-normal",
18    "hover:bg-accent hover:text-accent-foreground",
19    "data-[selected]:bg-primary data-[selected]:text-primary-foreground",
20    "data-[today]:bg-accent data-[today]:text-accent-foreground",
21    "data-[outside-range]:text-muted-foreground/50",
22    "data-[disabled]:text-muted-foreground data-[disabled]:opacity-50",
23    "data-[unavailable]:text-muted-foreground data-[unavailable]:line-through data-[unavailable]:opacity-40",
24    "data-[in-range]:rounded-none data-[in-range]:bg-accent data-[in-range]:text-accent-foreground",
25    "data-[range-start]:rounded-l-(--cell-radius) data-[range-start]:bg-primary data-[range-start]:text-primary-foreground",
26    "data-[range-end]:rounded-r-(--cell-radius) data-[range-end]:bg-primary data-[range-end]:text-primary-foreground",
27    "focus-visible:relative focus-visible:z-10 focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-1",
28    className
29  )}
30  {...props}
31>
32  {#if children}
33    {@render children()}
34  {/if}
35</DatePickerPrimitive.TableCellTrigger>
calendar/calendar-day-table.svelte
1<script lang="ts">
2  import { DatePicker as DatePickerPrimitive } from "@sprawlify/svelte/date-picker"
3  import { cn } from "@/lib/utils"
4  import CalendarDayButton from "./calendar-day-button.svelte"
5  import type { DateValue } from "@internationalized/date"
6  import type { Snippet } from "svelte"
7
8  interface CalendarDayTableProps {
9    weeks: DateValue[][]
10    weekDays: { short: string }[]
11    focusedMonth: number
12    showOutsideDays?: boolean
13    visibleRange?: { start: DateValue; end: DateValue }
14    cell?: Snippet<[DateValue]>
15  }
16
17  let {
18    weeks,
19    weekDays,
20    focusedMonth,
21    showOutsideDays = true,
22    visibleRange,
23    cell
24  }: CalendarDayTableProps = $props()
25</script>
26
27<DatePickerPrimitive.Table class="w-full border-collapse">
28  <DatePickerPrimitive.TableHead>
29    <DatePickerPrimitive.TableRow class="flex">
30      {#each weekDays as weekDay}
31        <DatePickerPrimitive.TableHeader
32          class="flex-1 rounded-(--cell-radius) text-[0.8rem] font-normal text-muted-foreground select-none"
33        >
34          {weekDay.short}
35        </DatePickerPrimitive.TableHeader>
36      {/each}
37    </DatePickerPrimitive.TableRow>
38  </DatePickerPrimitive.TableHead>
39  <DatePickerPrimitive.TableBody>
40    {#each weeks as week}
41      <DatePickerPrimitive.TableRow class="mt-2 flex w-full">
42        {#each week as day, dayIndex (dayIndex)}
43          {@const isOutside = day.month !== focusedMonth}
44          {#if !showOutsideDays && isOutside}
45            <td class="flex-1 p-0" aria-hidden="true"></td>
46          {:else}
47            <DatePickerPrimitive.TableCell
48              value={day}
49              {visibleRange}
50              class={cn(
51                "group/day relative aspect-square h-full w-full flex-1 rounded-(--cell-radius) p-0 text-center select-none",
52                "[&:last-child[data-selected]_div]:rounded-r-(--cell-radius)",
53                "[&:first-child[data-selected]_div]:rounded-l-(--cell-radius)"
54              )}
55            >
56              <CalendarDayButton>
57                {#if cell}
58                  {@render cell(day)}
59                {:else}
60                  {day.day}
61                {/if}
62              </CalendarDayButton>
63            </DatePickerPrimitive.TableCell>
64          {/if}
65        {/each}
66      </DatePickerPrimitive.TableRow>
67    {/each}
68  </DatePickerPrimitive.TableBody>
69</DatePickerPrimitive.Table>
calendar/calendar-day-view.svelte
1<script lang="ts">
2  import { DatePicker } from "@sprawlify/svelte/date-picker"
3  import CalendarSelectHeader from "./calendar-select-header.svelte"
4  import CalendarDayTable from "./calendar-day-table.svelte"
5  import type { DateValue } from "@internationalized/date"
6  import type { Snippet } from "svelte"
7
8  interface CalendarDayViewProps {
9    showOutsideDays?: boolean
10    header?: Snippet
11    cell?: Snippet<[DateValue]>
12  }
13
14  let {
15    showOutsideDays = true,
16    header,
17    cell
18  }: CalendarDayViewProps = $props()
19</script>
20
21<DatePicker.View view="day" class="flex flex-col gap-4">
22  <DatePicker.Context>
23    {#snippet render(api)}
24      {#if header}
25        {@render header()}
26      {:else}
27        <CalendarSelectHeader />
28      {/if}
29      <CalendarDayTable
30        weeks={api().weeks}
31        weekDays={api().weekDays}
32        focusedMonth={api().focusedValue.month}
33        {showOutsideDays}
34        {cell}
35      />
36    {/snippet}
37  </DatePicker.Context>
38</DatePicker.View>
calendar/calendar-dual-month-day-view.svelte
1<script lang="ts">
2  import { DatePicker } from "@sprawlify/svelte/date-picker"
3  import CalendarDayTable from "./calendar-day-table.svelte"
4  import Button from "../button/button.svelte"
5  import { ChevronLeft, ChevronRight } from "lucide-svelte"
6  import type { DateValue } from "@internationalized/date"
7  import type { Snippet } from "svelte"
8
9  interface CalendarDualMonthDayViewProps {
10    showOutsideDays?: boolean
11    cell?: Snippet<[DateValue]>
12  }
13
14  let {
15    showOutsideDays = true,
16    cell
17  }: CalendarDualMonthDayViewProps = $props()
18</script>
19
20<DatePicker.View view="day" class="flex flex-col gap-4">
21  <DatePicker.Context>
22    {#snippet render(api)}
23      {@const offset = api().getOffset({ months: 1 })}
24      <div class="flex gap-4 flex-row">
25        <!-- First month -->
26        <div class="flex flex-col gap-4">
27          <div class="relative flex w-full items-center justify-between gap-1">
28            <DatePicker.PrevTrigger>
29              {#snippet asChild(props)}
30                <Button
31                  variant="ghost"
32                  size="icon"
33                  class="size-(--cell-size) p-0 select-none aria-disabled:opacity-50"
34                  {...props()}
35                >
36                  <ChevronLeft class="size-4" />
37                </Button>
38              {/snippet}
39            </DatePicker.PrevTrigger>
40            <span class="text-sm font-medium select-none">
41              {api().visibleRangeText.start}
42            </span>
43            <div class="size-(--cell-size)"></div>
44          </div>
45          <CalendarDayTable
46            weeks={api().weeks}
47            weekDays={api().weekDays}
48            focusedMonth={api().focusedValue.month}
49            {showOutsideDays}
50            {cell}
51          />
52        </div>
53        <!-- Second month -->
54        <div class="flex flex-col gap-4">
55          <div class="relative flex w-full items-center justify-between gap-1">
56            <div class="size-(--cell-size)"></div>
57            <span class="text-sm font-medium select-none">
58              {api().visibleRangeText.end}
59            </span>
60            <DatePicker.NextTrigger>
61              {#snippet asChild(props)}
62                <Button
63                  variant="ghost"
64                  size="icon"
65                  class="size-(--cell-size) p-0 select-none aria-disabled:opacity-50"
66                  {...props()}
67                >
68                  <ChevronRight class="size-4" />
69                </Button>
70              {/snippet}
71            </DatePicker.NextTrigger>
72          </div>
73          <CalendarDayTable
74            weeks={offset.weeks}
75            weekDays={api().weekDays}
76            focusedMonth={offset.visibleRange.start.month}
77            {showOutsideDays}
78            visibleRange={offset.visibleRange}
79            {cell}
80          />
81        </div>
82      </div>
83    {/snippet}
84  </DatePicker.Context>
85</DatePicker.View>
calendar/calendar-month-view.svelte
1<script lang="ts">
2  import { DatePicker } from "@sprawlify/svelte/date-picker"
3  import CalendarSelectHeader from "./calendar-select-header.svelte"
4  import Button from "../button/button.svelte"
5  import type { Snippet } from "svelte"
6
7  interface CalendarMonthViewProps {
8    header?: Snippet
9    cell?: Snippet<[{ label: string; value: number }]>
10  }
11
12  let {
13    header,
14    cell
15  }: CalendarMonthViewProps = $props()
16</script>
17
18<DatePicker.View view="month" class="flex flex-col gap-4">
19  <DatePicker.Context>
20    {#snippet render(api)}
21      {#if header}
22        {@render header()}
23      {:else}
24        <CalendarSelectHeader />
25      {/if}
26      <DatePicker.Table class="w-full border-collapse">
27        <DatePicker.TableBody>
28          {#each api().getMonthsGrid({ columns: 4, format: "short" }) as months}
29            <DatePicker.TableRow class="mt-2 flex w-full">
30              {#each months as month}
31                <DatePicker.TableCell
32                  value={month.value}
33                  class="flex-1 p-0 text-center"
34                >
35                  <DatePicker.TableCellTrigger>
36                    {#snippet asChild(props)}
37                      <Button
38                        variant="ghost"
39                        class="w-full text-sm font-normal"
40                        {...props()}
41                      >
42                        {#if cell}
43                          {@render cell(month)}
44                        {:else}
45                          {month.label}
46                        {/if}
47                      </Button>
48                    {/snippet}
49                  </DatePicker.TableCellTrigger>
50                </DatePicker.TableCell>
51              {/each}
52            </DatePicker.TableRow>
53          {/each}
54        </DatePicker.TableBody>
55      </DatePicker.Table>
56    {/snippet}
57  </DatePicker.Context>
58</DatePicker.View>
calendar/calendar-preset-trigger.svelte
1<script lang="ts">
2  import { DatePicker } from "@sprawlify/svelte/date-picker";
3  import type { ComponentProps } from "svelte";
4
5  interface CalendarPresetTriggerProps
6    extends ComponentProps<typeof DatePicker.PresetTrigger> {}
7
8  let { children, ...props }: CalendarPresetTriggerProps = $props();
9</script>
10
11<DatePicker.PresetTrigger {...props}>
12  {@render children?.()}
13</DatePicker.PresetTrigger>
14
calendar/calendar-select-header.svelte
1<script lang="ts">
2  import { DatePicker } from "@sprawlify/svelte/date-picker";
3  import { Button } from "@/components/ui/button";
4  import { ChevronLeft, ChevronRight, ChevronDown } from "lucide-svelte";
5  import { cn } from "@/lib/utils";
6  import type { ComponentProps } from "svelte";
7
8  interface CalendarSelectHeaderProps {
9    buttonVariant?: ComponentProps<typeof Button>["variant"];
10    class?: string;
11  }
12
13  let { buttonVariant = "ghost", class: className }: CalendarSelectHeaderProps =
14    $props();
15</script>
16
17<DatePicker.ViewControl
18  class={cn(
19    "relative flex w-full items-center justify-between gap-1",
20    className,
21  )}
22>
23  <Button
24    variant={buttonVariant}
25    size="icon"
26    class="size-(--cell-size) p-0 select-none aria-disabled:opacity-50"
27  >
28    {#snippet asChild(props)}
29      <DatePicker.PrevTrigger {...props()}>
30        <ChevronLeft class="size-4" />
31      </DatePicker.PrevTrigger>
32    {/snippet}
33  </Button>
34
35  <div class="flex items-center gap-1">
36    <span class="relative">
37      <DatePicker.MonthSelect
38        class="appearance-none rounded-md bg-transparent py-1 pr-6 pl-2 text-sm font-medium outline-none hover:bg-accent focus-visible:ring-[3px] focus-visible:ring-ring/50"
39      />
40      <ChevronDown
41        class="pointer-events-none absolute top-1/2 right-1 size-3.5 -translate-y-1/2 text-muted-foreground"
42      />
43    </span>
44    <span class="relative">
45      <DatePicker.YearSelect
46        class="appearance-none rounded-md bg-transparent py-1 pr-6 pl-2 text-sm font-medium outline-none hover:bg-accent focus-visible:ring-[3px] focus-visible:ring-ring/50"
47      />
48      <ChevronDown
49        class="pointer-events-none absolute top-1/2 right-1 size-3.5 -translate-y-1/2 text-muted-foreground"
50      />
51    </span>
52  </div>
53  
54  <Button
55    variant={buttonVariant}
56    size="icon"
57    class="size-(--cell-size) p-0 select-none aria-disabled:opacity-50"
58  >
59    {#snippet asChild(props)}
60      <DatePicker.NextTrigger {...props()}>
61        <ChevronRight class="size-4" />
62      </DatePicker.NextTrigger>
63    {/snippet}
64  </Button>
65</DatePicker.ViewControl>
66
calendar/calendar-view-header.svelte
1<script lang="ts">
2  import { DatePicker } from "@sprawlify/svelte/date-picker"
3  import { Button } from "@/components/ui/button"
4  import { ChevronLeft, ChevronRight } from "lucide-svelte"
5  import type { ComponentProps } from "svelte";
6
7  interface CalendarViewHeaderProps {
8    buttonVariant?: ComponentProps<typeof Button>["variant"]
9  }
10
11  let { buttonVariant = "ghost" }: CalendarViewHeaderProps = $props()
12</script>
13
14<DatePicker.ViewControl class="relative flex w-full items-center justify-between gap-1">
15  <Button
16    variant={buttonVariant}
17    size="icon"
18    class="size-(--cell-size) p-0 select-none aria-disabled:opacity-50"
19  >
20    {#snippet asChild(props)}
21      <DatePicker.PrevTrigger {...props()}>
22        <ChevronLeft class="size-4" />
23      </DatePicker.PrevTrigger>
24    {/snippet}
25  </Button>
26
27  <DatePicker.ViewTrigger class="flex h-(--cell-size) items-center justify-center text-sm font-medium select-none">
28    <DatePicker.RangeText />
29  </DatePicker.ViewTrigger>
30
31  <Button
32    variant={buttonVariant}
33    size="icon"
34    class="size-(--cell-size) p-0 select-none aria-disabled:opacity-50"
35  >
36    {#snippet asChild(props)}
37      <DatePicker.NextTrigger {...props()}>
38        <ChevronRight class="size-4" />
39      </DatePicker.NextTrigger>
40    {/snippet}
41  </Button>
42</DatePicker.ViewControl>
calendar/calendar-year-view.svelte
1<script lang="ts">
2  import { DatePicker } from "@sprawlify/svelte/date-picker"
3  import CalendarSelectHeader from "./calendar-select-header.svelte"
4  import Button from "../button/button.svelte"
5  import type { Snippet } from "svelte"
6
7  interface CalendarYearViewProps {
8    header?: Snippet
9    cell?: Snippet<[{ label: string; value: number }]>
10  }
11
12  let {
13    header,
14    cell
15  }: CalendarYearViewProps = $props()
16</script>
17
18<DatePicker.View view="year" class="flex flex-col gap-4">
19  <DatePicker.Context>
20    {#snippet render(api)}
21      {#if header}
22        {@render header()}
23      {:else}
24        <CalendarSelectHeader />
25      {/if}
26      <DatePicker.Table class="w-full border-collapse">
27        <DatePicker.TableBody>
28          {#each api().getYearsGrid({ columns: 4 }) as years}
29            <DatePicker.TableRow class="mt-2 flex w-full">
30              {#each years as year}
31                <DatePicker.TableCell
32                  value={year.value}
33                  class="flex-1 p-0 text-center"
34                >
35                  <DatePicker.TableCellTrigger>
36                    {#snippet asChild(props)}
37                      <Button
38                        variant="ghost"
39                        class="w-full text-sm font-normal"
40                        {...props()}
41                      >
42                        {#if cell}
43                          {@render cell(year)}
44                        {:else}
45                          {year.label}
46                        {/if}
47                      </Button>
48                    {/snippet}
49                  </DatePicker.TableCellTrigger>
50                </DatePicker.TableCell>
51              {/each}
52            </DatePicker.TableRow>
53          {/each}
54        </DatePicker.TableBody>
55      </DatePicker.Table>
56    {/snippet}
57  </DatePicker.Context>
58</DatePicker.View>
calendar/calendar.svelte
1<script lang="ts">
2  import { cn } from "@/lib/utils"
3  import { DatePicker as DatePickerPrimitive } from "@sprawlify/svelte/date-picker"
4  import type { ComponentProps, Snippet } from "svelte"
5  import CalendarDayView from "./calendar-day-view.svelte"
6  import CalendarDualMonthDayView from "./calendar-dual-month-day-view.svelte"
7  import CalendarMonthView from "./calendar-month-view.svelte"
8  import CalendarYearView from "./calendar-year-view.svelte"
9
10  interface CalendarProps extends Omit<ComponentProps<typeof DatePickerPrimitive.Root>, "inline"> {
11    showOutsideDays?: boolean
12    children?: Snippet
13  }
14
15  let {
16    class: className,
17    showOutsideDays = true,
18    children,
19    numOfMonths,
20    ...props
21  }: CalendarProps = $props()
22
23  const isDualMonth = () => numOfMonths && numOfMonths >= 2
24</script>
25
26<DatePickerPrimitive.Root inline {numOfMonths} {...props}>
27  <div
28    data-slot="calendar"
29    class={cn(
30      "group/calendar w-fit bg-background p-2 [--cell-radius:var(--radius-md)] [--cell-size:--spacing(7)] in-data-[slot=card-content]:bg-transparent in-data-[slot=popover-content]:bg-transparent",
31      className
32    )}
33  >
34    {#if children}
35      {@render children()}
36    {:else}
37      {#if isDualMonth()}
38        <CalendarDualMonthDayView {showOutsideDays} />
39      {:else}
40        <CalendarDayView {showOutsideDays} />
41      {/if}
42      <CalendarMonthView />
43      <CalendarYearView />
44    {/if}
45  </div>
46</DatePickerPrimitive.Root>
calendar/index.ts
1export { default as Calendar } from "./calendar.svelte";2export { default as CalendarDayView } from "./calendar-day-view.svelte";3export { default as CalendarDualMonthDayView } from "./calendar-dual-month-day-view.svelte";4export { default as CalendarDayTable } from "./calendar-day-table.svelte";5export { default as CalendarMonthView } from "./calendar-month-view.svelte";6export { default as CalendarYearView } from "./calendar-year-view.svelte";7export { default as CalendarViewHeader } from "./calendar-view-header.svelte";8export { default as CalendarSelectHeader } from "./calendar-select-header.svelte";9export { default as CalendarDayButton } from "./calendar-day-button.svelte";10export { default as CalendarPresetTrigger } from "./calendar-preset-trigger.svelte";11

Update the import paths to match your project setup.

Usage

1import { Calendar } from "@/components/ui/calendar"
1<script module lang="ts">
2  let date = $state([new CalendarDate(2026, 3, 21)])
3
4  function handleValueChange(details) {
5    date = details.value
6  }
7</script>
8
9<Calendar
10  selectionMode="single"
11  value={date}
12  onValueChange={handleValueChange}
13  class="rounded-lg border"
14/>

Examples

Basic

1<script module lang="ts">
2  import { Calendar } from "@/components/ui/calendar"
3</script>
4

Calendar Booked Dates

1<script module lang="ts">
2  import { Calendar } from "@/components/ui/calendar"
3  import { Card, CardContent } from "@/components/ui/card"
4  import { CalendarDate, type DateValue } from "@internationalized/date"

Date And Time Picker

1<script module lang="ts">
2  import { Calendar } from "@/components/ui/calendar"
3  import { Card, CardContent, CardFooter } from "@/components/ui/card"
4  import { Field, FieldGroup, FieldLabel } from "@/components/ui/field"

Presets

1<script module lang="ts">
2  import { Button } from "@/components/ui/button"
3  import {
4    Calendar,

Range Calendar

1<script module lang="ts">
2  import { Calendar } from "@/components/ui/calendar"
3  import { Card, CardContent } from "@/components/ui/card"
4  import { CalendarDate, type DateValue } from "@internationalized/date"

Views

1<script>
2  import {
3    Calendar,
4    CalendarDayView,

On This Page

InstallationUsageExamplesBasicCalendar Booked DatesDate And Time PickerPresetsRange CalendarViews

Get PRO

Need premium blocks and templates? Upgrade to PRO and get access.