| Su | Mo | Tu | We | Th | Fr | Sa |
|---|---|---|---|---|---|---|
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개 이상의 캘린더 블록 컬렉션을 제공합니다.
모든 캘린더 블록은 블록 라이브러리 페이지에서 확인하세요.
설치
bits-ui와 @internationalized/date를 설치하세요:
다음 코드를 프로젝트에 복사하여 붙여넣으세요.
소개
<Calendar /> 컴포넌트는 Bits UI Calendar 컴포넌트를 기반으로 구축되었으며, @internationalized/date 패키지를 사용하여 날짜를 처리합니다.
범위 캘린더를 찾고 있다면 Range Calendar 컴포넌트를 확인하세요.
날짜 선택기
<Calendar /> 컴포넌트를 사용하여 날짜 선택기를 만들 수 있습니다. 자세한 내용은 Date Picker 페이지를 참조하세요.
예제
범위 캘린더
| Su | Mo | Tu | We | Th | Fr | Sa |
|---|---|---|---|---|---|---|
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 |
| Su | Mo | Tu | We | Th | Fr | Sa |
|---|---|---|---|---|---|---|
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}
/> 월 및 연도 선택기
| Su | Mo | Tu | We | Th | Fr | Sa |
|---|---|---|---|---|---|---|
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 /> 컴포넌트로 업그레이드할 수 있습니다:
기존 파일을 덮어쓸 것인지 묻는 메시지가 표시되면 Yes를 선택하세요. Calendar 컴포넌트를 변경한 경우 새 버전과 변경 사항을 병합해야 합니다.
블록 설치
Calendar 컴포넌트를 업그레이드한 후 다음과 같이 새 블록을 추가할 수 있습니다:
이렇게 하면 최신 버전의 캘린더 블록이 추가됩니다.