Calendar

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

2026년 5월
examples/krds/calendar-demo.tsx
"use client"

import * as React from "react"

설치

pnpm dlx shadcn@latest add https://krdscn.gwlee.kr/r/calendar.json

사용법

import { Calendar } from "@/components/ui/calendar"
const [date, setDate] = React.useState<Date | undefined>(new Date())

return (
  <Calendar
    mode="single"
    selected={date}
    onSelect={setDate}
    className="rounded-lg border"
  />
)

React DayPicker 문서를 참고하세요.

KRDS 정렬

  • 분류: React DayPicker 기반 shadcn Calendar API를 유지하는 KRDS-styled 날짜 선택 primitive입니다. mode, selected, onSelect, classNames 등 DayPicker prop을 그대로 사용할 수 있습니다.
  • KRDS 적용: month caption, navigation buttons, selected/range/today/disabled states가 KRDS date control token과 surface에 맞춰 조정되어 있습니다.
  • 사용 권장: 단독 달력은 날짜 선택 UI로 사용하고, 입력 필드와 달력 팝오버가 함께 필요한 경우에는 composed date-input 패턴을 사용하세요.

개요

Calendar 컴포넌트는 React DayPicker를 기반으로 합니다.

날짜 선택기

<Calendar>는 날짜 선택기를 구성하는 기반 컴포넌트입니다. 입력 필드와 함께 쓰는 패턴은 Date Input 문서를 참고하세요.

페르시아 / 히즈리 / 잘랄리 캘린더

페르시아/히즈리/잘랄리 캘린더가 필요하면 components/ui/calendar.tsx에서 react-day-picker import를 react-day-picker/persian으로 교체합니다.

- import { DayPicker } from "react-day-picker"
+ import { DayPicker } from "react-day-picker/persian"
2025년 6월
examples/krds/calendar-hijri.tsx
"use client"

import * as React from "react"

선택된 날짜(시간대 포함)

CalendartimeZone prop을 받아 사용자의 현지 시간대 기준으로 날짜를 표시하고 선택할 수 있습니다.

export function CalendarWithTimezone() {
  const [date, setDate] = React.useState<Date | undefined>(undefined)
  const [timeZone, setTimeZone] = React.useState<string | undefined>(undefined)

  React.useEffect(() => {
    setTimeZone(Intl.DateTimeFormat().resolvedOptions().timeZone)
  }, [])

  return (
    <Calendar
      mode="single"
      selected={date}
      onSelect={setDate}
      timeZone={timeZone}
    />
  )
}

참고: 선택한 날짜가 하루씩 어긋나 보이면 timeZone prop이 사용자의 현지 시간대로 설정되어 있는지 확인하세요.

클라이언트에서 감지하는 이유: 서버와 브라우저의 시간대가 다를 수 있으므로 useEffect 안에서 Intl.DateTimeFormat().resolvedOptions().timeZone을 사용해 hydration mismatch를 피합니다.

예제

기본

기본 Calendar 컴포넌트입니다. 예제에서는 className="rounded-lg border"로 스타일을 지정했습니다.

2026년 5월
examples/krds/calendar-basic.tsx
"use client"

import { Calendar } from "@/components/ui/calendar"

범위 선택

mode="range" prop으로 범위 선택을 활성화합니다.

2026년 1월
2026년 2월
examples/krds/calendar-range.tsx
"use client"

import * as React from "react"

월/연도 선택

captionLayout="dropdown"을 사용하면 월/연도 드롭다운을 표시할 수 있습니다.

2026년 5월
examples/krds/calendar-caption.tsx
"use client"

import { Calendar } from "@/components/ui/calendar"

빠른 선택

2026년 5월
examples/krds/calendar-presets.tsx
"use client"

import * as React from "react"

날짜와 시간 선택

2026년 5월
examples/krds/calendar-time.tsx
"use client"

import * as React from "react"

예약된 날짜

2026년 2월
examples/krds/calendar-booked-dates.tsx
"use client"

import * as React from "react"

셀 크기 조정

2026년 12월
examples/krds/calendar-custom-days.tsx
"use client"

import * as React from "react"

--cell-size CSS 변수로 날짜 셀 크기를 조정할 수 있습니다. breakpoint별 값을 지정해 반응형으로 만들 수도 있습니다.

<Calendar
  mode="single"
  selected={date}
  onSelect={setDate}
  className="rounded-lg border [--cell-size:--spacing(11)] md:[--cell-size:--spacing(12)]"
/>

고정값을 사용할 수도 있습니다.

<Calendar
  mode="single"
  selected={date}
  onSelect={setDate}
  className="rounded-lg border [--cell-size:2.75rem] md:[--cell-size:3rem]"
/>

주차 표시

showWeekNumber를 사용하면 주차를 표시할 수 있습니다.

2026년 2월
06
07
08
09
examples/krds/calendar-week-numbers.tsx
"use client"

import * as React from "react"

RTL

RTL 지원을 활성화하려면 RTL 설정 가이드를 참고하세요.

페르시아/히즈리/잘랄리 캘린더 사용법은 위 섹션을 참고하세요.

مايو 2026
examples/krds/calendar-rtl.tsx
"use client"

import * as React from "react"

RTL 환경에서는 locale을 import하고 locale, dir prop을 함께 전달합니다.

import { arSA } from "react-day-picker/locale"

;<Calendar
  mode="single"
  selected={date}
  onSelect={setDate}
  locale={arSA}
  dir="rtl"
/>

API 참조

자세한 API는 React DayPicker 문서를 참고하세요.

변경 내역

RTL 지원

이전 버전의 Calendar에서 locale 지원을 추가하려면 다음 변경사항을 적용하세요.

Locale 타입을 import합니다.

react-day-picker import에 Locale을 추가합니다.

  import {
    DayPicker,
    getDefaultClassNames,
    type DayButton,
+   type Locale,
  } from "react-day-picker"

Calendar 컴포넌트에 locale prop을 추가합니다.

컴포넌트 props에 locale prop을 추가합니다.

  function Calendar({
    className,
    classNames,
    showOutsideDays = true,
    captionLayout = "label",
    buttonVariant = "ghost",
+   locale,
    formatters,
    components,
    ...props
  }: React.ComponentProps<typeof DayPicker> & {
    buttonVariant?: React.ComponentProps<typeof Button>["variant"]
  }) {

locale을 DayPicker에 전달합니다.

DayPicker 컴포넌트에 locale prop을 전달합니다.

    <DayPicker
      showOutsideDays={showOutsideDays}
      className={cn(...)}
      captionLayout={captionLayout}
+     locale={locale}
      formatters={{
        formatMonthDropdown: (date) =>
-         date.toLocaleString("default", { month: "short" }),
+         date.toLocaleString(locale?.code, { month: "short" }),
        ...formatters,
      }}

CalendarDayButton이 locale을 받도록 수정합니다.

CalendarDayButton 시그니처를 수정하고 locale을 전달합니다.

  function CalendarDayButton({
    className,
    day,
    modifiers,
+   locale,
    ...props
- }: React.ComponentProps<typeof DayButton>) {
+ }: React.ComponentProps<typeof DayButton> & { locale?: Partial<Locale> }) {

CalendarDayButton의 날짜 포맷을 수정합니다.

날짜 포맷에 locale?.code를 사용합니다.

    <Button
      variant="ghost"
      size="icon"
-     data-day={day.date.toLocaleDateString()}
+     data-day={day.date.toLocaleDateString(locale?.code)}
      ...
    />

DayButton 컴포넌트에 locale을 전달합니다.

DayButton 컴포넌트 사용부에 locale prop을 전달합니다.

      components={{
        ...
-       DayButton: CalendarDayButton,
+       DayButton: ({ ...props }) => (
+         <CalendarDayButton locale={locale} {...props} />
+       ),
        ...
      }}

Update RTL-aware CSS classes.

Replace directional classes with logical properties for better RTL support:

  // In the day classNames:
- [&:last-child[data-selected=true]_button]:rounded-r-(--cell-radius)
+ [&:last-child[data-selected=true]_button]:rounded-e-(--cell-radius)
- [&:nth-child(2)[data-selected=true]_button]:rounded-l-(--cell-radius)
+ [&:nth-child(2)[data-selected=true]_button]:rounded-s-(--cell-radius)
- [&:first-child[data-selected=true]_button]:rounded-l-(--cell-radius)
+ [&:first-child[data-selected=true]_button]:rounded-s-(--cell-radius)

  // In range_start classNames:
- rounded-l-(--cell-radius) ... after:right-0
+ rounded-s-(--cell-radius) ... after:end-0

  // In range_end classNames:
- rounded-r-(--cell-radius) ... after:left-0
+ rounded-e-(--cell-radius) ... after:start-0

  // In CalendarDayButton className:
- data-[range-end=true]:rounded-r-(--cell-radius)
+ data-[range-end=true]:rounded-e-(--cell-radius)
- data-[range-start=true]:rounded-l-(--cell-radius)
+ data-[range-start=true]:rounded-s-(--cell-radius)

After applying these changes, you can use the locale prop to provide locale-specific formatting:

import { enUS } from "react-day-picker/locale"

;<Calendar mode="single" selected={date} onSelect={setDate} locale={enUS} />