Datepicker

달력에서 날짜를 고른다. 내부는 react-day-picker 이고 값은 Date 객체로 주고받는다. 기간은 DateRangePicker, 여러 날짜는 DateMultiplePicker 다.

import { Datepicker, DateRangePicker, DateMultiplePicker } from "@nui-kit/react";
// 서브패스로 좁힐 때
import { Datepicker } from "@nui-kit/react/datepicker";
import "@nui-kit/react/styles/datepicker.css";   // 온디맨드일 때
import { Datepicker, DateRangePicker } from "@nui-kit/react";

<Datepicker selected={date} onSelectedChange={setDate} />
<DateRangePicker selected={range} onSelectedChange={setRange} />
controlled 전용이다. selected onSelectedChange 를 소비자가 소유한다. react-hook-form 을 쓴다면 @nui-kit/react/rhf RHFDatepicker 계열을 쓴다.

직접 입력

DatepickerDateRangePicker 는 입력창에 날짜를 직접 칠 수 있다. 달력이 있어도 입력 필드를 읽기 전용으로 만들지 않는다는 KRDS 기준(가이드 675쪽)을 따른다. 형식은 displayFormat (기본 yyyy.MM.dd), 기간은 2026.09.01 - 2026.09.05 처럼 앞뒤에 공백을 둔 대시로 잇는다.

이렇게 치면이렇게 된다
2026.9.5값으로 읽고, 입력창을 벗어나면 2026.09.05 로 정리한다
읽을 수 없는 글자 · 없는 날짜(2026.02.31) · 절반만 친 기간입력창을 벗어나는 순간 치기 전 값으로 되돌린다. 에러 메시지는 띄우지 않는다 — 검증은 소비자 몫이다
달력이 막아 둔 날짜 · 이동할 수 없는 연도받지 않는다. dayPickerProps disabled · startMonth · endMonth 를 타이핑에도 똑같이 적용한다
입력창을 비움값이 undefined 가 된다

치는 동안 달력은 그 날짜의 달로 따라 이동한다. 예전처럼 달력으로만 값을 받고 싶으면 isTextInputBlocked 를 준다. DateMultiplePicker 는 아직 읽기 전용이다 — 날짜 목록의 구분자 규칙이 따로 필요해 다음 단계로 미뤘다.

형식 안내는 infoMessage 로 적는다. 플레이스홀더만으로 형식을 알리지 않는다 — 값을 치기 시작하면 사라지기 때문이다.

기간은 최소 2일이다. 같은 날을 두 번 눌러 하루짜리 기간을 만들 수 없다 — 두 번째 클릭은 선택 해제로 처리된다 (dayPickerProps.min 기본값 1). 하루도 허용하려면 dayPickerProps={{ min: 0 }} 를 넘긴다.

기본 — 날짜 하나

입력창을 클릭하거나 캘린더 버튼을 누르면 달력이 열린다. 날짜를 고르면 자동으로 닫힌다.

현재 값: 없음

<Datepicker selected={date} onSelectedChange={setDate} />

선택하면 닫힌다 (shouldCloseOnSelect 기본값)

기간 선택

시작일과 종료일을 차례로 고른다. 둘 다 정해지기 전까지는 onSelectedChangeundefined 를 넘긴다 — 불완전한 기간이 폼에 들어가지 않는다.

현재 값: 없음

<DateRangePicker selected={range} onSelectedChange={setRange} />

from → to 순서로 선택

여러 날짜

날짜를 여러 개 고른다. 선택할 때마다 달력이 닫히면 불편하므로 열린 상태를 유지한다.

현재 값: 없음

<DateMultiplePicker selected={dates} onSelectedChange={setDates} />

선택해도 닫히지 않는다

선택 가능 범위 제한

dayPickerProps 로 react-day-picker 에 그대로 전달한다. 오늘 이전 날짜를 막은 예다.

dayPickerProps={{ disabled: { before: new Date() } }}

dayPickerProps.disabled 로 과거 차단

상태

<Datepicker selected={date} errorMessage="날짜를 선택해주세요" />

disabled

readOnly — 값은 보이지만 달력이 열리지 않는다

날짜를 선택해주세요.

errorMessage — 아이콘과 텍스트를 함께 표시하고 aria-describedby 로 연결한다

잘리는 상자 안에서 — hasPortal

달력은 기본적으로 제자리에 뜬다. 조상에 overflow: hidden 이 있으면 잘려서 날짜를 고를 수 없다. 카드나 팝업 안에 넣을 때 hasPortal 을 켜면 달력이 body 로 나가 잘리지 않는다. Tooltip · Select 의 같은 이름 prop 과 한 규칙이다.

기본상자에 잘린다
hasPortal상자를 벗어난다
<Datepicker hasPortal />   // 잘리는 상자 안에서

Field 와 함께

영업일만 선택할 수 있습니다.

react-hook-form 연동

@nui-kit/react/rhfRHFDatepicker · RHFDateRangePicker · RHFDateMultiplePicker control 만 넘기면 값과 에러를 스스로 소유한다. selected · name · onBlur 는 타입에서 제외되어 중복 소유가 생기지 않는다.

시작일과 종료일을 모두 선택해야 합니다.

여러 날짜를 고를 수 있습니다.

{
  "visitDate": null,
  "stay": null,
  "extraDates": null,
  "isValid": false,
  "errors": {}
}

mode: "onChange"

스타일 커스터마이징

공개 CSS 변수로 달력 치수와 팝업 외형을 조정한다.

색은 컴포넌트별로 열지 않는다. 한 곳만 바꾸려면 className 을, 화면 전체를 바꾸려면 브랜드 프리셋을 쓴다.

컴포넌트변수기본값비고
Datepicker 계열--nui-datepicker--border-widthvar(--nui-border-width-1) · 3자리를 함께 움직인다
--nui-datepicker--day-button-sizevar(--nui-size-control-md)
--nui-datepicker--day-radiusvar(--nui-radius-1_5)
--nui-datepicker--day-sizevar(--nui-size-control-option)
--nui-datepicker--dropdown-radiusvar(--nui-radius-3)
react-day-picker 의 기본 CSS 는 불러올 필요가 없다. 이 컴포넌트는 라이브러리의 classNames 를 통째로 nui-daypicker__* 로 갈아끼운 뒤 우리 CSS 로 그린다. 그래서 소비자 프로젝트가 같은 라이브러리를 따로 쓰더라도 서로 간섭하지 않는다. 달력 세부 스타일을 직접 손보려면 nui-daypicker__day 처럼 우리 클래스를 대상으로 하면 된다.

접근성

API

세 컴포넌트는 selected 의 타입만 다르고 나머지는 같다. dayPickerPropsreact-day-picker 의 설정(disabled, startMonth, locale 등)을 그대로 전달한다.

Datepicker

이름타입기본값설명
calendarButtonTitlestring
calendarLabelstring캘린더 팝업의 접근 이름. i18n 을 위해 열어둔다.
classNamestring | undefined
clearButtonTitlestring지우기 버튼의 접근 이름. 소비자의 어휘·언어로 바꿀 수 있어야 한다 (a11y.md §9)
dayPickerPropsDatepickerDayPickerProps<TDayPickerProps>
defaultIsCalendarOpenboolean
disabledboolean
displayFormatstring
dropdownClassNamestring
errorMessagestring
formatDisplayValueDatepickerBaseSingleProps["formatDisplayValue"]formatSingleDateValue
getDefaultMonthDatepickerBaseSingleProps["getDefaultMonth"]getSingleDefaultMonth
getShouldCloseOnSelectDatepickerBaseSingleProps["getShouldCloseOnSelect"]getShouldCloseSingleOnSelect
hasPortalboolean달력을 `body` 로 내보내 **잘리는 조상을 탈출한다.** 기본 배치는 제자리(`absolute`)라 조상에 `overflow: hidden` 이 있으면 달력이 잘려 날짜를 고를 수 없다. 카드·팝업 안에 넣을 때 켠다. `Tooltip`·`Select` 의 같은 이름 prop 과 한 규칙이다.
idstring
infoMessagestring
inputRefRef<HTMLInputElement>
isClearableboolean
isTextInputBlockedboolean
onClear() => void
onSelectedChange(selected: TSelected | undefined) => void
parseDisplayValue((options: { text: string; displayFormat: string; locale: Locale; isDateAllowed: (date: Date) => boolean; isFinal?: boolean | undefined; }) => Date | undefined) | undefinedparseSingleDateValue글자 → 값. `formatDisplayValue` 의 역방향이다. **이것을 넘긴 모드만 직접 입력이 열린다** (KRDS 가이드 675쪽 접근성 01 — 선택기가 있어도 입력 필드를 읽기 전용으로 만들지 않는다). 넘기지 않으면 예전처럼 `readonly` 입력이다.
placeholderstring
readOnlyboolean
selectedTSelected | undefined
shouldCloseOnSelectboolean
unitstring

DatepickerProps 에서 자동 생성됨 (components/Datepicker/Datepicker.tsx). 표준 DOM 속성 297개는 그대로 전달되며 표에서 생략했다.

DateRangePicker

이름타입기본값설명
calendarButtonTitlestring
calendarLabelstring캘린더 팝업의 접근 이름. i18n 을 위해 열어둔다.
classNamestring | undefined
clearButtonTitlestring지우기 버튼의 접근 이름. 소비자의 어휘·언어로 바꿀 수 있어야 한다 (a11y.md §9)
dayPickerPropsDatepickerDayPickerProps<TDayPickerProps>
defaultIsCalendarOpenbooleanfalse
disabledboolean
displayFormatstring
dropdownClassNamestring
errorMessagestring
formatDisplayValueDateRangePickerBaseProps["formatDisplayValue"]formatRangeDateValue
getDefaultMonthDateRangePickerBaseProps["getDefaultMonth"]getRangeDefaultMonth
getShouldCloseOnSelectDateRangePickerBaseProps["getShouldCloseOnSelect"]getShouldCloseRangeOnSelect
hasPortalboolean달력을 `body` 로 내보내 **잘리는 조상을 탈출한다.** 기본 배치는 제자리(`absolute`)라 조상에 `overflow: hidden` 이 있으면 달력이 잘려 날짜를 고를 수 없다. 카드·팝업 안에 넣을 때 켠다. `Tooltip`·`Select` 의 같은 이름 prop 과 한 규칙이다.
idstring
infoMessagestring
inputRefRef<HTMLInputElement>
isClearableboolean
isTextInputBlockedboolean
onClear() => void
onSelectedChange(selected: TSelected | undefined) => void
parseDisplayValue((options: { text: string; displayFormat: string; locale: Locale; isDateAllowed: (date: Date) => boolean; isFinal?: boolean | undefined; }) => DateRange | undefined) | undefinedparseRangeDateValue글자 → 값. `formatDisplayValue` 의 역방향이다. **이것을 넘긴 모드만 직접 입력이 열린다** (KRDS 가이드 675쪽 접근성 01 — 선택기가 있어도 입력 필드를 읽기 전용으로 만들지 않는다). 넘기지 않으면 예전처럼 `readonly` 입력이다.
placeholderstring
readOnlyboolean
selectedTSelected | undefined
shouldCloseOnSelectboolean
unitstring

DateRangePickerProps 에서 자동 생성됨 (components/Datepicker/DateRangePicker.tsx). 표준 DOM 속성 297개는 그대로 전달되며 표에서 생략했다.

DateMultiplePicker

이름타입기본값설명
calendarButtonTitlestring
calendarLabelstring캘린더 팝업의 접근 이름. i18n 을 위해 열어둔다.
classNamestring | undefined
clearButtonTitlestring지우기 버튼의 접근 이름. 소비자의 어휘·언어로 바꿀 수 있어야 한다 (a11y.md §9)
dayPickerPropsDatepickerDayPickerProps<TDayPickerProps>
defaultIsCalendarOpenboolean
disabledboolean
displayFormatstring
dropdownClassNamestring
errorMessagestring
formatDisplayValueDateMultiplePickerBaseProps["formatDisplayValue"]formatMultipleDateValue
getDefaultMonthDateMultiplePickerBaseProps["getDefaultMonth"]getMultipleDefaultMonth
getShouldCloseOnSelectDateMultiplePickerBaseProps["getShouldCloseOnSelect"]getShouldCloseMultipleOnSelect
hasPortalboolean달력을 `body` 로 내보내 **잘리는 조상을 탈출한다.** 기본 배치는 제자리(`absolute`)라 조상에 `overflow: hidden` 이 있으면 달력이 잘려 날짜를 고를 수 없다. 카드·팝업 안에 넣을 때 켠다. `Tooltip`·`Select` 의 같은 이름 prop 과 한 규칙이다.
idstring
infoMessagestring
inputRefRef<HTMLInputElement>
isClearableboolean
onClear() => void
onSelectedChange(selected: TSelected | undefined) => void
placeholderstring
readOnlyboolean
selectedTSelected | undefined
shouldCloseOnSelectboolean
unitstring

DateMultiplePickerProps 에서 자동 생성됨 (components/Datepicker/DateMultiplePicker.tsx). 표준 DOM 속성 297개는 그대로 전달되며 표에서 생략했다.