Skip to content
shadcn.io
shadcn.io is not affiliated with official shadcn/ui

Shadcn Date Input for React and Tailwind

Segmented input for dates and times.

mmddyyyy

Installation

bunx --bun shadcn@latest add https://kit.dev/r/date-input.json

Anatomy

DateInput
├── DateInputLabel (optional)
└── Control (baked in)
    ├── SegmentGroup
    │   └── Segment
    ├── separator (range)
    ├── SegmentGroup (range)
    ├── HiddenInput (baked in)
    └── Clear (optional)

shadcn.io Date Input wraps Ark Date Input. Control, segments, and hidden inputs are baked in. useDateInput is the machine hook for DateInputRootProvider; useDateInputContext / DateInputContext is in-tree.

Prefer Field for the visible label. For a calendar popup, use Date Picker. For an inline calendar, use Calendar.

Usage

import { parseDate } from "@ark-ui/react/date-picker";
import { DateInput } from "@/components/ui/date-input";
<DateInput />

value / defaultValue are always a DateValue[]. Build values with parseDate (from @ark-ui/react/date-picker or @internationalized/date).

shadcn.io defaults shouldForceLeadingZeros to true (Ark: false). selectionMode is "single". granularity is "day". Size is set on the control (sm h-7, md h-8, lg h-9).

Controlled

Control the value with value and onValueChange. { value: DateValue[]; valueAsString: string[] }.

Default value

Set the initial date with defaultValue and parseDate.

Root Provider

Use useDateInput with DateInputRootProvider when you need the API outside the tree. Pass machine options (defaultValue, granularity, selectionMode, …) to useDateInput(), not to the provider. Layout (size, showClear, separator, selectionMode for the second group) still goes on the provider.

States

Invalid

Disabled

Read-only

Sizes

Size is set on DateInput. sm is h-7, md is h-8, lg is h-9.

Small

Medium

Large

Examples

With Field

Field wires the label and helper text to the control.

Granularity

granularity is the smallest unit shown: day, hour, minute, or second.

Time only

Set granularity to minute or second and pass a formatter that only includes time fields. hourCycle is 12 or 24.

Range

Set selectionMode="range" for start and end groups. Customize the divider with separator (string or node).

Custom separator

Min and max

Restrict committed dates with min and max (DateValue).

Leading zeros

shadcn.io forces leading zeros by default. Set shouldForceLeadingZeros={false} to follow the locale.

With clear button

Pass showClear. The clear control is shown when there is a value.

Context

Read value and focus with DateInputContext or useDateInputContext.

Guides

Date Input vs Date Picker vs Calendar

Date InputDate PickerCalendar
EntryTyped segmentsInput + popup calendarInline calendar
Use whenKeyboard date/time, no popupPick from a calendar fieldAlways-visible calendar

Values

Selected dates are always DateValue[] (CalendarDate, CalendarDateTime, or ZonedDateTime).

import { parseDate } from "@ark-ui/react/date-picker";

<DateInput defaultValue={[parseDate("2024-04-04")]} />

Range:

<DateInput
  defaultValue={[parseDate("2024-04-04"), parseDate("2024-04-10")]}
  selectionMode="range"
/>

onValueChange details: { value: DateValue[]; valueAsString: string[] }.

Time

granularity of hour / minute / second adds time segments. Pair with hourCycle and optionally useDateFormatter for a time-only field (no year/month/day in the formatter).

Root provider layout

selectionMode on DateInputRootProvider must match useDateInput({ selectionMode }) so the second group and hidden input render in range mode.

const dateInput = useDateInput({
  selectionMode: "range",
});

<DateInputRootProvider selectionMode="range" value={dateInput} />

API Reference

shadcn.io wraps Ark UI Date Input. Defaults below are shadcn.io Date Input values. shouldForceLeadingZeros is true here (Ark: false).

asChild merges props onto a single child element.

DateInput

Root. Renders a div. Bakes control, segments, hidden input, and optional clear.

PropTypeDefaultDescription
size"sm" | "md" | "lg""md"Control height. sm is h-7, md is h-8, lg is h-9.
showClearbooleanfalseShow a clear button when there is a value.
separatorstring | ReactNode"-"Divider between start and end groups in range mode.
selectionMode"single" | "range""single"One date, or start and end.
shouldForceLeadingZerosbooleantrueAlways pad month, day, and hour.
granularity"day" | "hour" | "minute" | "second""day"Smallest unit shown.
hourCycle12 | 24-12- or 24-hour clock. Locale default when omitted.
formatterDateFormatter-Custom formatter (for example time-only).
valueDateValue[]-Controlled selected dates.
defaultValueDateValue[]-Uncontrolled initial dates.
placeholderValueDateValue-Controlled placeholder date used for empty segments.
defaultPlaceholderValueDateValue-Uncontrolled placeholder date.
minDateValue-Minimum committed date.
maxDateValue-Maximum committed date.
isDateUnavailable(date: DateValue, locale: string) => boolean-Marks matching committed dates invalid.
disabledboolean-Disable the input.
readOnlyboolean-Non-editable, still focusable.
invalidboolean-Mark as invalid.
requiredboolean-Mark as required.
namestring-name on the hidden input.
formstring-Associated form id.
localestring"en-US"BCP 47 locale for formatting.
timeZonestring"UTC"Time zone.
hideTimeZonebooleanfalseHide the time zone segment on ZonedDateTime values.
createCalendar(identifier: CalendarIdentifier) => Calendar-Non-Gregorian calendars.
translationsIntlTranslations-{ placeholder?: (locale) => Record<EditableSegmentType, string> }.
idsPartial<{ root: string; label: (index: number) => string; control: string; segmentGroup: (index: number) => string; hiddenInput: (index: number) => string }>-Element ids for composition.
idstring-Unique id for the machine.
format(date: DateValue, details: FormatDateDetails) => string-Custom string conversion.
onValueChange(details: ValueChangeDetails) => void-{ value, valueAsString }.
onPlaceholderChange(details: PlaceholderChangeDetails) => void-Placeholder changed. { value, valueAsString, placeholderValue }.
onFocusChange(details: FocusChangeDetails) => void-{ focused }.
asChildbooleanfalseRender the child element instead of a div.
classNamestring-Class names on the root.

Children render above the baked control (for example DateInputLabel or DateInputContext).

AttributeDescription
data-slotdate-input
data-scopedate-input
data-partroot
data-size"sm", "md", or "lg"
data-invalidPresent when invalid
data-disabledPresent when disabled
data-readonlyPresent when read-only

DateInputLabel

Optional visible label. Prefer Field (FieldLabel). Renders a label.

PropTypeDefaultDescription
asChildbooleanfalseMerge onto a single child.
classNamestring-Class names on the label.
AttributeDescription
data-slotdate-input-label
data-scopedate-input
data-partlabel
data-disabledPresent when disabled
data-invalidPresent when invalid
data-readonlyPresent when read-only
data-requiredPresent when required

Control (baked in)

Input Group wrapping the segments and optional clear button. Not a separate export.

AttributeDescription
data-slotdate-input-control
data-scopedate-input
data-partcontrol
data-disabledPresent when disabled
data-invalidPresent when invalid
data-readonlyPresent when read-only

The field wrapper is data-slot="date-input-field". The clear button is data-slot="date-input-clear" with aria-label="Clear date".

Segment group (baked in)

One group for single, two for range (index 0 and 1).

AttributeDescription
data-slotdate-input-segment-group
data-scopedate-input
data-partsegment-group

Segment (baked in)

Each date part (year, month, day, hour, minute, second, dayPeriod, literal, …). Renders a span.

AttributeDescription
data-slotdate-input-segment
data-scopedate-input
data-partsegment
data-typeSegment type (day, month, literal, …)
data-placeholder-shownPresent when the segment is still a placeholder
data-readonlyPresent when read-only
data-disabledPresent when disabled
data-invalidPresent when invalid

Hidden input (baked in)

One native input per group for form submission (index 0, and 1 in range mode).

AttributeDescription
data-scopedate-input
data-parthidden-input

DateInputRootProvider

Root alternative that takes the API from useDateInput. Renders a div with the same baked control.

PropTypeDefaultDescription
valueUseDateInputReturnrequiredReturn value of useDateInput().
size"sm" | "md" | "lg""md"Control height.
showClearbooleanfalseShow a clear button when there is a value.
separatorstring | ReactNode"-"Range divider.
selectionMode"single" | "range""single"Must match useDateInput({ selectionMode }).
asChildbooleanfalseRender the child element instead of a div.
classNamestring-Class names on the root.

Pass defaultValue, granularity, selectionMode, shouldForceLeadingZeros, and other machine options to useDateInput(), not to DateInputRootProvider. useDateInput already defaults shouldForceLeadingZeros to true.

AttributeDescription
data-slotdate-input
data-scopedate-input
data-partroot

useDateInput

Creates the date input API for DateInputRootProvider. Same options as DateInput except layout-only props (size, showClear, separator). Applies shouldForceLeadingZeros: true unless you override it.

const dateInput = useDateInput({
  defaultValue: [parseDate("2024-04-04")],
});

dateInput.focus();

DateInputContext / useDateInputContext

Render-prop or hook access to date input state. Use inside DateInput or DateInputRootProvider.

PropertyTypeDescription
focusedbooleanWhether a segment is focused.
disabledbooleanWhether the input is disabled.
invalidbooleanWhether the input is invalid.
valueDateValue[]Selected dates.
valueAsDateDate[]Selected dates as Date objects.
valueAsStringstring[]Selected dates as strings.
placeholderValueDateValuePlaceholder date for empty segments.
displayValuesIncompleteDate[]Per-group editing state.
focus() => voidFocus the first segment.
setValue(values: DateValue[]) => voidSet the selection.
clearValue() => voidClear the selection.
getSegments(props?: { index?: number }) => DateSegment[]Segments for a group.
getSegmentState(props: { segment: DateSegment; index?: number }) => SegmentStateeditable, focused, readonly for one segment.

DateInputContext children: (context) => ReactNode.

Accessibility

Complies with the Date Picker WAI-ARIA spinbutton pattern on each editable segment. Label the control with Field (FieldLabel) or DateInputLabel. Hidden inputs submit the value with the form.

Keyboard support

KeyDescription
ArrowLeft / ArrowRightMove to the previous or next segment. In range mode, moves between start and end groups.
ArrowUp / ArrowDownIncrement or decrement the focused segment.
PageUp / PageDownLarger step on the focused segment (for example 7 days, 15 minutes).
Home / EndJump to the minimum or maximum for the focused segment.
Digit keysType a value into the focused numeric segment. Advances when the field is full.
Backspace / DeleteClear the focused segment.
Tab / Shift + TabMove between segments, then out of the control.