이 사이트는 shadcn-svelte 공식 문서의 한국어 번역입니다.
6.9k

Calendar

Previous Next

사용자가 날짜를 선택할 수 있는 캘린더 컴포넌트입니다.

Docs API Reference
SuMoTuWeThFrSa
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<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 shadow-sm"
  captionLayout="dropdown"
/>

블록

캘린더 컴포넌트를 구축하는 데 사용할 수 있는 30개 이상의 캘린더 블록 컬렉션을 제공합니다.

모든 캘린더 블록은 블록 라이브러리 페이지에서 확인하세요.

설치

pnpm dlx shadcn-svelte@latest add calendar

소개

<Calendar /> 컴포넌트는 Bits UI Calendar 컴포넌트를 기반으로 구축되었으며, @internationalized/date 패키지를 사용하여 날짜를 처리합니다.

범위 캘린더를 찾고 있다면 Range Calendar 컴포넌트를 확인하세요.

날짜 선택기

<Calendar /> 컴포넌트를 사용하여 날짜 선택기를 만들 수 있습니다. 자세한 내용은 Date Picker 페이지를 참조하세요.

예제

범위 캘린더

June 2025
SuMoTuWeThFrSa
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
1
2
3
4
5
July 2025
SuMoTuWeThFrSa
29
30
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
1
2
<script lang="ts">
  import Calendar from "$lib/components/ui/calendar/calendar.svelte";
  import { CalendarDate } from "@internationalized/date";
 
  let value = $state<CalendarDate | undefined>(new CalendarDate(2025, 6, 12));
</script>
 
<Calendar
  type="single"
  bind:value
  class="rounded-lg border shadow-sm"
  numberOfMonths={2}
/>

월 및 연도 선택기

SuMoTuWeThFrSa
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
1
2
3
4
5
<script lang="ts">
  import Calendar from "$lib/components/ui/calendar/calendar.svelte";
  import * as Select from "$lib/components/ui/select/index.js";
  import { Label } from "$lib/components/ui/label/index.js";
  import { CalendarDate } from "@internationalized/date";
  import type { ComponentProps } from "svelte";
 
  let value = $state<CalendarDate>(new CalendarDate(2025, 6, 12));
  let dropdown =
    $state<ComponentProps<typeof Calendar>["captionLayout"]>("dropdown");
 
  const dropdownOptions = [
    {
      label: "Month and Year",
      value: "dropdown"
    },
    {
      label: "Month Only",
      value: "dropdown-months"
    },
    {
      label: "Year Only",
      value: "dropdown-years"
    }
  ];
 
  const selectedDropdown = $derived(
    dropdownOptions.find((option) => option.value === dropdown)?.label ??
      "Dropdown"
  );
 
  const id = $props.id();
</script>
 
<div class="flex flex-col gap-4">
  <Calendar
    type="single"
    bind:value
    class="rounded-lg border shadow-sm"
    captionLayout={dropdown}
  />
  <div class="flex flex-col gap-3">
    <Label for="{id}-dropdown" class="px-1">Dropdown</Label>
    <Select.Root type="single" bind:value={dropdown}>
      <Select.Trigger id="{id}-dropdown" size="sm" class="bg-background w-full">
        {selectedDropdown}
      </Select.Trigger>
      <Select.Content align="center">
        {#each dropdownOptions as option (option.value)}
          <Select.Item value={option.value}>{option.label}</Select.Item>
        {/each}
      </Select.Content>
    </Select.Root>
  </div>
</div>

생년월일 선택기

<script lang="ts">
  import Calendar from "$lib/components/ui/calendar/calendar.svelte";
  import * as Popover from "$lib/components/ui/popover/index.js";
  import { Button } from "$lib/components/ui/button/index.js";
  import { Label } from "$lib/components/ui/label/index.js";
  import ChevronDownIcon from "@lucide/svelte/icons/chevron-down";
  import {
    getLocalTimeZone,
    today,
    type CalendarDate
  } from "@internationalized/date";
 
  const id = $props.id();
 
  let open = $state(false);
  let value = $state<CalendarDate | undefined>();
</script>
 
<div class="flex flex-col gap-3">
  <Label for="{id}-date" class="px-1">Date of birth</Label>
  <Popover.Root bind:open>
    <Popover.Trigger id="{id}-date">
      {#snippet child({ props })}
        <Button
          {...props}
          variant="outline"
          class="w-48 justify-between font-normal"
        >
          {value
            ? value.toDate(getLocalTimeZone()).toLocaleDateString()
            : "Select date"}
          <ChevronDownIcon />
        </Button>
      {/snippet}
    </Popover.Trigger>
    <Popover.Content class="w-auto overflow-hidden p-0" align="start">
      <Calendar
        type="single"
        bind:value
        captionLayout="dropdown"
        onValueChange={() => {
          open = false;
        }}
        maxValue={today(getLocalTimeZone())}
      />
    </Popover.Content>
  </Popover.Root>
</div>

날짜 및 시간 선택기

<script lang="ts">
  import Calendar from "$lib/components/ui/calendar/calendar.svelte";
  import * as Popover from "$lib/components/ui/popover/index.js";
  import { Button } from "$lib/components/ui/button/index.js";
  import { Label } from "$lib/components/ui/label/index.js";
  import { Input } from "$lib/components/ui/input/index.js";
  import ChevronDownIcon from "@lucide/svelte/icons/chevron-down";
  import { getLocalTimeZone } from "@internationalized/date";
  import type { CalendarDate } from "@internationalized/date";
 
  const id = $props.id();
 
  let open = $state(false);
  let value = $state<CalendarDate | undefined>();
</script>
 
<div class="flex gap-4">
  <div class="flex flex-col gap-3">
    <Label for="{id}-date" class="px-1">Date</Label>
    <Popover.Root bind:open>
      <Popover.Trigger id="{id}-date">
        {#snippet child({ props })}
          <Button
            {...props}
            variant="outline"
            class="w-32 justify-between font-normal"
          >
            {value
              ? value.toDate(getLocalTimeZone()).toLocaleDateString()
              : "Select date"}
            <ChevronDownIcon />
          </Button>
        {/snippet}
      </Popover.Trigger>
      <Popover.Content class="w-auto overflow-hidden p-0" align="start">
        <Calendar
          type="single"
          bind:value
          onValueChange={() => {
            open = false;
          }}
          captionLayout="dropdown"
        />
      </Popover.Content>
    </Popover.Root>
  </div>
  <div class="flex flex-col gap-3">
    <Label for="{id}-time" class="px-1">Time</Label>
    <Input
      type="time"
      id="{id}-time"
      step="1"
      value="10:30:00"
      class="bg-background appearance-none [&::-webkit-calendar-picker-indicator]:hidden [&::-webkit-calendar-picker-indicator]:appearance-none"
    />
  </div>
</div>

자연어 선택기

이 컴포넌트는 chrono-node 라이브러리를 사용하여 자연어 날짜를 파싱합니다.

Your post will be published on February 03, 2026.
<script lang="ts">
  import { Label } from "$lib/components/ui/label/index.js";
  import * as Popover from "$lib/components/ui/popover/index.js";
  import { Button } from "$lib/components/ui/button/index.js";
  import { Calendar } from "$lib/components/ui/calendar/index.js";
  import { Input } from "$lib/components/ui/input/index.js";
  import CalendarIcon from "@lucide/svelte/icons/calendar";
  import { parseDate } from "chrono-node";
  import {
    CalendarDate,
    getLocalTimeZone,
    type DateValue
  } from "@internationalized/date";
  import { untrack } from "svelte";
 
  function formatDate(date: DateValue | undefined) {
    if (!date) return "";
 
    return date.toDate(getLocalTimeZone()).toLocaleDateString("en-US", {
      day: "2-digit",
      month: "long",
      year: "numeric"
    });
  }
 
  const id = $props.id();
 
  let open = $state(false);
  let inputValue = $state("In 2 days");
  let value = $state<DateValue | undefined>(
    untrack(() => {
      const date = parseDate(inputValue);
      if (date)
        return new CalendarDate(
          date.getFullYear(),
          date.getMonth() + 1,
          date.getDate()
        );
      return undefined;
    })
  );
</script>
 
<div class="flex flex-col gap-3">
  <Label for="{id}-date" class="px-1">Schedule Date</Label>
  <div class="relative flex gap-2">
    <Input
      id="date"
      bind:value={
        () => inputValue,
        (v) => {
          inputValue = v;
          const date = parseDate(v);
          if (date) {
            value = new CalendarDate(
              date.getFullYear(),
              date.getMonth() + 1,
              date.getDate()
            );
          }
        }
      }
      placeholder="Tomorrow or next week"
      class="bg-background pe-10"
      onkeydown={(e) => {
        if (e.key === "ArrowDown") {
          e.preventDefault();
          open = true;
        }
      }}
    />
    <Popover.Root bind:open>
      <Popover.Trigger id="{id}-date-picker">
        {#snippet child({ props })}
          <Button
            {...props}
            variant="ghost"
            class="absolute end-2 top-1/2 size-6 -translate-y-1/2"
          >
            <CalendarIcon class="size-3.5" />
            <span class="sr-only">Select date</span>
          </Button>
        {/snippet}
      </Popover.Trigger>
      <Popover.Content class="w-auto overflow-hidden p-0" align="end">
        <Calendar
          type="single"
          bind:value
          captionLayout="dropdown"
          onValueChange={(v) => {
            inputValue = formatDate(v);
            open = false;
          }}
        />
      </Popover.Content>
    </Popover.Root>
  </div>
  <div class="text-muted-foreground px-1 text-sm">
    Your post will be published on
    <span class="font-medium">{formatDate(value)}</span>.
  </div>
</div>

업그레이드 가이드

다음 명령을 실행하여 최신 버전의 <Calendar /> 컴포넌트로 업그레이드할 수 있습니다:

pnpm dlx shadcn-svelte@latest add calendar

기존 파일을 덮어쓸 것인지 묻는 메시지가 표시되면 Yes를 선택하세요. Calendar 컴포넌트를 변경한 경우 새 버전과 변경 사항을 병합해야 합니다.

블록 설치

Calendar 컴포넌트를 업그레이드한 후 다음과 같이 새 블록을 추가할 수 있습니다:

pnpm dlx shadcn-svelte@latest add calendar-02

이렇게 하면 최신 버전의 캘린더 블록이 추가됩니다.