Date picker

캘린더를 통해 특정 날짜를 선택할 수 있게 해주는 요소로 월 단위 탐색, 연도 선택, 오늘 날짜 표시 등의 기능을 제공하며, 날짜 형식 오류를 방지하고 일관된 데이터 입력을 보장합니다.

Basic usage

사용자가 YYYY. MM. DD순으로 날짜를 입력할 수 있는 폼을 제공합니다.

  • locale, timezone을 지정할 수 있습니다.
  • 기본적으로 TextField 컴포넌트를 사용합니다.
  • 항상 유효한 Date 값을 가지고 있지는 않아요. Invalid date 상태를 가질 수 있습니다.
import { DatePicker } from '@wanteddev/wds';

const Demo = () => {
  return (
    <DatePicker width="40%" />
  )
}

export default Demo;

Format

날짜가 표시되는 format 을 지정할 수 있습니다. 아래 format을 모두 사용할 수 있습니다.

  • YYYY, YY
  • M, MM, MMM, MMMM
  • D, DD
  • H, HH, h, hh
  • m, mm
  • s, ss
  • A, a
import { DatePicker, FlexBox } from '@wanteddev/wds';

const Demo = () => {
  return (
    <FlexBox flexDirection="column" alignItems="center" gap="20px" sx={{ width: '100%' }}>
      <DatePicker format="MMMM.DD" width="40%" />

      <DatePicker format="MM/YYYY" width="40%" />
      
      <DatePicker format="YYYY-MM-DD" width="40%" />
    </FlexBox>
  )
}

export default Demo;

Localization

  • Locale
    • 기본적으로 locale은 ko-KR 으로 지정되어 있습니다.
    • Intl API 에서 지원하는 언어를 모두 import 없이 사용할 수 있습니다.
  • Timezone
    • 기본적으로 timezone은 사용자의 환경에 따라 결정돼요.
    • dayjs 에서 지원하는 timezone과 UTC 를 모두 import 없이 사용할 수 있습니다.
import { DatePicker, FlexBox } from '@wanteddev/wds';

const Demo = () => {
  return (
    <FlexBox flexDirection="column" alignItems="center" gap="20px" sx={{ width: '100%' }}>
      <DatePicker format="MMM/DD hh:mm:ss" width="40%" defaultValue={new Date()} locale="ko-KR" timezone="Asia/Seoul" />
      <DatePicker format="MMM/DD hh:mm:ss" width="40%" defaultValue={new Date()} locale="en-US" timezone="UTC" />
    </FlexBox>
  )
}

export default Demo;

Views

views, defaultView, view, onViewChange 로 선택 UI를 조정할 수 있습니다.

'year' | 'month' | 'day' 를 지원합니다.

import { DatePicker, FlexBox } from '@wanteddev/wds';

const Demo = () => {

  return (
    <FlexBox flexDirection="column" gap="20px">
      <DatePicker
        width="300px"
        format="YYYY.MM"
        views={['year', 'month']}
      />
      <DatePicker
        width="300px"
        format="YYYY"
        views={['year']}
      />
      <DatePicker
        width="300px"
        format="YYYY MM DD"
        defaultView="year"
        views={['year', 'day']}
      />
    </FlexBox>
  )
}

export default Demo;

Action area

PickerActionArea 를 사용하여 하단 영역의 버튼을 커스텀 할 수 있습니다.

총 4가지 variant를 사용할 수 있습니다.

  • now
  • accept
  • cancel
  • reset

Checkbox 컴포넌트와 함께 사용할 때에는 disableLastUnitClickClose prop 을 사용하여 더욱 자연스러운 동작을 구현할 수 있습니다.

import { DatePicker, FlexBox, PickerActionArea, PickerActionAreaButton, FormField, FormControl, FormLabel, Checkbox } from '@wanteddev/wds';

const Demo = () => {
  return (
    <FlexBox flexDirection="column" gap="20px">
      <DatePicker
        width="300px"
        placeholder="현재, 확인"
        actionArea={
          <PickerActionArea>
            <PickerActionAreaButton variant="now">현재</PickerActionAreaButton>
            <PickerActionAreaButton variant="accept">확인</PickerActionAreaButton>
          </PickerActionArea>
        }
      />

      <DatePicker
        width="300px"
        placeholder="취소, 초기화"
        actionArea={
          <PickerActionArea>
            <PickerActionAreaButton variant="cancel">취소</PickerActionAreaButton>
            <PickerActionAreaButton variant="reset">초기화</PickerActionAreaButton>
          </PickerActionArea>
        }
      />


      <DatePicker
        width="300px"
        placeholder="checkbox, 확인"
        disableLastUnitClickClose
        actionArea={
          <PickerActionArea>
            <FormField
              gap="8px"
              flexDirection="row"
            >
              <FormControl>
                <Checkbox size="medium" />
              </FormControl>
              <FormLabel
                sx={{ padding: "1px 0px" }}
              >
                텍스트
              </FormLabel>
            </FormField>
            <PickerActionAreaButton variant="accept">확인</PickerActionAreaButton>
          </PickerActionArea>
        }
      />
    </FlexBox>
  )
}

export default Demo;

Validation

  • min, max prop 을 사용하여 최소값과 최대값을 설정할 수 있습니다.
  • 키보드 조작을 통해서는 범위를 벗어나는 값을 입력할 수 있습니다.
  • Invalid date 상태를 가질 수 있습니다.
import { DatePicker, FlexBox, FormErrorMessage, FormField, FormLabel, FormControl } from '@wanteddev/wds';
import { Controller, useForm } from 'react-hook-form';

const minDate = new Date('2000-01-01');
const maxDate = new Date('2025-12-31');

const Demo = () => {
  const form = useForm({
    defaultValues: {
      date: new Date(),
    },
    mode: 'onChange'
  });

  return (
    <FlexBox as="form">
      <Controller
        control={form.control}
        name="date"
        rules={{
          validate: (value) => {
            if (isNaN(new Date(value).getTime())) {
              return '유효한 날짜를 입력해주세요.';
            }

            if (new Date(value).getTime() < minDate.getTime()) {
              return '최소 날짜는 2000년 1월 1일 이상이어야 합니다.';
            }

            if (new Date(value).getTime() > maxDate.getTime()) {
              return '최대 날짜는 2025년 12월 31일 이하이어야 합니다.';
            }
            
          return true;
          },
        }}
        render={({ field, formState }) => (
          <FormField>
            <FormLabel>날짜</FormLabel>

            <FormControl>
              <DatePicker
                min={minDate}
                max={maxDate}
                format="YYYY.MM.DD"
                width="300px"
                inputRef={field.ref}
                value={field.value}
                onChange={field.onChange}
                invalid={!!formState.errors.date}
              />
            </FormControl>

            <FormErrorMessage>{formState.errors.date?.message}</FormErrorMessage>
          </FormField>
        )}
      />
    </FlexBox>
  )
}

export default Demo;

Form field

Form과 관련된 컴포넌트와 함께 사용할 수 있습니다.

  • FormField
  • FormControl
  • FormLabel
  • FormMessage
  • FormErrorMessage

Helper Message

import { FlexBox, FormField, FormControl, FormLabel, FormMessage, FormErrorMessage, DatePicker } from '@wanteddev/wds';

const Demo = () => {
  return (
    <FlexBox justifyContent="center" sx={{ width: '100%' }}>
      <FormField sx={{ width: '40%' }}>
        <FormLabel required sx={{ padding: '1px 0px' }}>Label</FormLabel>
        <FormControl>
          <DatePicker width="100%" />
        </FormControl>
        <FormMessage>Helper Message</FormMessage>
      </FormField>
    </FlexBox>
  )
}

export default Demo;

Customize

TextField 컴포넌트 대신 커스텀 컴포넌트를 사용할 수 있습니다.

react 18.3 미만의 버전을 사용 중이라면, forwardRef 를 필수로 작성해주세요.

import { DatePicker, DatePickerFieldProps, Chip, FlexBox } from '@wanteddev/wds';
import { forwardRef, useState } from 'react';

const CustomChipField = forwardRef<HTMLDivElement, DatePickerFieldProps>(
  ({ value, onClick, inputRef, invalid, ...props }, ref) => {
    return (
      <Chip ref={ref} onClick={onClick}>
        {value || 'YYYY.MM.DD'}
      </Chip>
    )
  }
)

const CustomFieldField = forwardRef<HTMLDivElement, DatePickerFieldProps>(
  ({ trailingContent, inputRef, invalid, ...props }, ref) => {
    return (
      <FlexBox ref={ref} gap="10px" alignItems="center">
        <input ref={inputRef} {...props} />
        {trailingContent}
      </FlexBox>
    )
  }
)

const Demo = () => {
  const [open1, setOpen1] = useState(false);
  const [open2, setOpen2] = useState(false);

  return (
    <FlexBox flexDirection="column" gap="12px" alignItems="center" sx={{ width: '100%' }}>
      <DatePicker
        width="40%"
        input={CustomChipField}
        open={open1}
        onOpenChange={setOpen1}
        onClick={() => setOpen1(true)}
      />
      <DatePicker
        width="40%"
        input={CustomFieldField}
        open={open2}
        onOpenChange={setOpen2}
      />
    </FlexBox>
  )
}

export default Demo;

Controlled

기본적으로 비제어 컴포넌트로 동작합니다.

value, onChange prop 을 사용하면 제어 컴포넌트로 동작합니다.

제어 컴포넌트와 비제어 컴포넌트는 React 공식 문서 를 참조해주세요.

import { DatePicker, FlexBox } from '@wanteddev/wds';
import { useState } from 'react';

const Demo = () => {
  const [value, setValue] = useState(new Date());

  return (
    <FlexBox alignItems="center" flexDirection="column" gap="12px" alignItems="center" sx={{ width: '100%' }}>
      <DatePicker defaultValue={new Date()} width="40%" />
      <DatePicker value={value} onChange={setValue} width="40%" />
    </FlexBox>
  )
}

export default Demo;

Accessibility

WAI-ARIA Datepicker dialog 패턴 대부분을 지원합니다.

  • Form Field 와 함께 사용할 때 더욱 명확한 접근성 속성을 주입할 수 있습니다.

API

DatePicker

NameTypesdefaultValue
open
boolean
-
defaultOpen
boolean
-
onOpenChange
(state: boolean) => void
-
contentProps
WithSxProps<Merge<ComponentProps<typeof PopperContent>, ComponentPropsWithoutRef<typeof FocusScope>>>
-
format
string
"YYYY.MM.DD"
inputRef
null | (instance: null | HTMLInputElement) => void | () => VoidOrUndefinedOnly | RefObject
-
input
{ propTypes?: any; contextType?: React.Context<any>; defaultProps?: Partial<DatePickerFieldProps>; displayName?: string; getDerivedStateFromProps?: React.GetDerivedStateFromProps<DatePickerFieldProps, any>; getDerivedStateFromError?: React.GetDerivedStateFromError<DatePickerFieldProps, any> } | (props: { ref?: React.Ref<HTMLDivElement> | undefined; inputRef?: React.Ref<HTMLInputElement> | undefined; } & Omit<DefaultComponentPropsInternal<Omit<Omit<Omit<DefaultComponentPropsInternal<TextFieldProps, "input">, "ref"> & React.RefAttributes<HTMLInputElement>, "ref">, "wrapperRef">, "input">, "ref" | "inputRef">) => null | string | number | bigint | false | true | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | Iterable<React.ReactNode> | React.ReactPortal | Promise<AwaitedReactNode> | Promise<React.ReactNode>
-
actionArea
ReactNode
-
disableLastUnitClickClose
boolean
-
view
"year" | "month" | "day"
-
defaultValue
null | string | Date
-
children
ReactNode
-
onChange
(value: null | string | Date) => void
-
onReset
(event: React.SyntheticEvent<HTMLDivElement, Event>) => void & (prevValue: string) => void
-
disabled
boolean
-
sx
SxProp
-
xl
Merge<Pick<TextFieldDefaultProps, "height" | "width">, { sx?: CSSInterpolation; }> | undefined
-
lg
Merge<Pick<TextFieldDefaultProps, "height" | "width">, { sx?: CSSInterpolation; }> | undefined
-
md
Merge<Pick<TextFieldDefaultProps, "height" | "width">, { sx?: CSSInterpolation; }> | undefined
-
sm
Merge<Pick<TextFieldDefaultProps, "height" | "width">, { sx?: CSSInterpolation; }> | undefined
-
xs
Merge<Pick<TextFieldDefaultProps, "height" | "width">, { sx?: CSSInterpolation; }> | undefined
-
leadingContent
ReactNode
-
trailingContent
ReactNode
-
value
null | string | Date
-
positive
boolean
-
height
Property.Height<string | number> | undefined
-
width
Property.Width<string | number> | undefined
-
invalid
boolean
-
defaultView
"year" | "month" | "day"
-
onViewChange
(view: "year" | "month" | "day") => void
-
views
Array<"year" | "month" | "day">
-
max
null | string | Date
-
min
null | string | Date
-
locale
string
"ko-KR"
timezone
string
-
onChangeComplete
(value: null | string | Date) => void
-
readOnly
boolean
-
yearsOrder
"desc" | "asc"
-
trailingButton
ReactNode
-

PickerActionArea

NameTypesdefaultValue
children
ReactNode
-
variant
"strong" | "neutral" | "compact" | "cancel"
-
extra
boolean
-
caption
ReactNode
-
extraContent
ReactNode
-
compactContent
ReactNode
-
background
boolean
-
divider
boolean
-
sx
SxProp
-

PickerActionAreaButton

NameTypesdefaultValue
as
ElementType
-
variant
"cancel" | "accept" | "reset" | "now"
-
color
"primary" | "assistive"
-
children
ReactNode
-
disabled
boolean
-
sx
SxProp
-
xl
Merge<Pick<TextButtonDefaultProps, "size">, { sx?: CSSInterpolation; }> | undefined
-
lg
Merge<Pick<TextButtonDefaultProps, "size">, { sx?: CSSInterpolation; }> | undefined
-
md
Merge<Pick<TextButtonDefaultProps, "size">, { sx?: CSSInterpolation; }> | undefined
-
sm
Merge<Pick<TextButtonDefaultProps, "size">, { sx?: CSSInterpolation; }> | undefined
-
xs
Merge<Pick<TextButtonDefaultProps, "size">, { sx?: CSSInterpolation; }> | undefined
-
disableInteraction
boolean
-
leadingContent
ReactNode
-
trailingContent
ReactNode
-
loading
boolean
-
disableLoadingPreventEvents
boolean
-
size
"small" | "medium"
-

© 2026 Wanted Lab, Inc.