Skip to main content

Date picker

Let people enter or choose a single date with a date picker and calendar controls.

Examples

Breakpoints

Mobile

320px

Mobile Lg

480px

Tablet

640px

Desktop

1024px
MM/DD/YYYY, September 1 through October 31, 2026. Try an unavailable or impossible date.
date-picker-demo.tsx
import { DatePicker } from '@uswds-tailwind/react'
const minDate = new Date(2026, 8, 1)
const maxDate = new Date(2026, 9, 31)
const initialDate = [new Date(2026, 8, 28)]
export default function DatePickerDemo() {
return (
<div>
<label htmlFor="appointment-date" className="block">Appointment date</label>
<div id="appointment-date-hint" className="text-gray-50">
MM/DD/YYYY, September 1 through October 31, 2026. Try an unavailable or impossible date.
</div>
<DatePicker.Root
ids={{ input: () => 'appointment-date' }}
min={minDate}
max={maxDate}
defaultValue={initialDate}
className="max-w-mobile-lg"
>
<DatePicker.Control>
<DatePicker.Input id="appointment-date" name="appointmentDate" aria-describedby="appointment-date-hint" />
<DatePicker.Trigger />
</DatePicker.Control>
<DatePicker.Content>
<DatePicker.View view="day">
{({ api }) => (
<>
<DatePicker.ViewControl>
<DatePicker.PrevTrigger unit="year" />
<DatePicker.PrevTrigger />
<div className="flex grow justify-center">
<DatePicker.ViewTrigger view="month" />
<DatePicker.ViewTrigger view="year" />
</div>
<DatePicker.NextTrigger />
<DatePicker.NextTrigger unit="year" />
</DatePicker.ViewControl>
<DatePicker.Table>
<DatePicker.TableHead>
<DatePicker.TableRow>
{api.weekDays.map((day, index) => (
<DatePicker.TableHeader key={day.long} day={day} index={index} />
))}
</DatePicker.TableRow>
</DatePicker.TableHead>
<DatePicker.TableBody>
{api.weeks.map((week, row) => (
<DatePicker.TableRow key={row}>
{week.map(cell => (
<DatePicker.TableCell key={cell.getTime()} value={cell}>
<DatePicker.TableCellTrigger value={cell}>
{cell.getDate()}
</DatePicker.TableCellTrigger>
</DatePicker.TableCell>
))}
</DatePicker.TableRow>
))}
</DatePicker.TableBody>
</DatePicker.Table>
</>
)}
</DatePicker.View>
<DatePicker.View view="month">
{({ api }) => (
<DatePicker.Table>
<DatePicker.TableBody>
{api.monthRows.map((row, rowIndex) => (
<DatePicker.TableRow key={rowIndex}>
{row.map(month => (
<DatePicker.TableCell key={month}>
<DatePicker.TableCellTrigger value={month}>
{api.monthLabels[month]}
</DatePicker.TableCellTrigger>
</DatePicker.TableCell>
))}
</DatePicker.TableRow>
))}
</DatePicker.TableBody>
</DatePicker.Table>
)}
</DatePicker.View>
<DatePicker.View view="year">
{({ api }) => (
<>
<DatePicker.PrevTrigger view="year" />
<DatePicker.Table>
<DatePicker.TableBody>
{api.yearRows.map((row, rowIndex) => (
<DatePicker.TableRow key={rowIndex}>
{row.map(year => (
<DatePicker.TableCell key={year}>
<DatePicker.TableCellTrigger value={year}>
{year}
</DatePicker.TableCellTrigger>
</DatePicker.TableCell>
))}
</DatePicker.TableRow>
))}
</DatePicker.TableBody>
</DatePicker.Table>
<DatePicker.NextTrigger view="year" />
</>
)}
</DatePicker.View>
</DatePicker.Content>
<DatePicker.Status />
</DatePicker.Root>
</div>
)
}

Dates and bounds

The visible input uses MM/DD/YYYY. Set min and max on DatePicker.Root when only part of the calendar is available. A typed date must be a real calendar date within those bounds; an empty optional field remains valid.

Provide a visible format hint and a label for the input. The calendar trigger lets people choose a date without typing, while the same bounds apply to both paths. Malformed or out-of-bounds text sets aria-invalid; leaving the field or pressing Enter updates native validity.

min and max use Date values; defaultValue is an array of dates. Create calendar dates with local year, month, and day values, such as new Date(2026, 8, 28) for September 28. onValueChange returns selected Date values and ISO strings in valueAsString.

Component API

Root supplies date state. Group Input and Trigger in Control, with Content containing the calendar views and tables. The visible input uses MM/DD/YYYY; valueAsString in onValueChange uses ISO YYYY-MM-DD.

Root<div>

Owns selected dates, bounds, and calendar state.

PropTypeDescription
selectionModesingle | rangeSelects one date or a date range.
minDateEarliest selectable calendar date.
maxDateLatest selectable calendar date.
defaultValue(Date | undefined)[]Initial uncontrolled selected dates. In range mode, index 0 is the start and index 1 is the end; either slot may be undefined.
value(Date | undefined)[]Controlled selected dates. In range mode, index 0 is the start and index 1 is the end; either slot may be undefined.
disabledbooleanDisables date selection.
onValueChange({ value: (Date | undefined)[]; valueAsString: string[]; view: day | month | year }) => voidReceives proposed dates, ISO YYYY-MM-DD strings, and the view. Unset range endpoints keep their slots as undefined and empty strings. Update value in the callback when controlling the selection.
onOpenChange({ open }) => voidReceives calendar visibility changes.
idsDatepicker ElementIdsOverrides generated part IDs. Input, label, and trigger IDs use callbacks, such as input: index => index === 0 ? "start-date" : "end-date".
defaultDateDateInitial navigation fallback when no date is selected. Does not select or submit a value.
rangeAnchorDateFixed peer date for range markers and preview; independent of the selected value.
readOnlybooleanMakes the visible inputs read-only and prevents calendar editing.
requiredbooleanMarks the visible inputs required.
localestringLocale for month and weekday labels. Defaults to English.
AttributeValuePurpose
data-stateopen | closedStyle the open and closed states.
data-disabledPresent when disabledStyle the disabled state.

Control<div>

Groups an Input and Trigger.

Input<input>

Receives a typed MM/DD/YYYY date. Malformed or out-of-bounds text sets aria-invalid; blur and Enter update native validity.

PropTypeDescription
index0 | 1Endpoint to edit or open: 0 for start, 1 for end. Use the same index on each input and its trigger.

Trigger<button>

Opens the shared calendar for the indexed input.

PropTypeDescription
index0 | 1Endpoint to edit or open: 0 for start, 1 for end. Use the same index on each input and its trigger.
AttributeValuePurpose
data-stateopen | closedStyle the open and closed states.

Content<div>

Place the calendar views inside the popover.

AttributeValuePurpose
data-stateopen | closedStyle the open and closed states.

ViewControl<div>

Groups the calendar navigation controls.

PrevTrigger<button>

Moves backward by one month or year in day view, or by twelve years in year view.

PropTypeDescription
viewday | yearNavigation view. Defaults to day.
unitmonth | yearDay-view navigation step. Defaults to month; omit in year view.

NextTrigger<button>

Moves forward by one month or year in day view, or by twelve years in year view.

PropTypeDescription
viewday | yearNavigation view. Defaults to day.
unitmonth | yearDay-view navigation step. Defaults to month; omit in year view.

ViewTrigger<button>

Opens month or year selection.

PropTypeDescription
viewmonth | year (required)View to open.

View<div>

Renders the day, month, or year view.

PropTypeDescription
viewday | month | year (required)Selects the displayed calendar view.
childrenReactNode | ({ api }) => ReactNodeCan render from the date picker API.

Table<table>

Lays out the calendar grid.

TableHead<thead>

Contains weekday headings.

TableRow<tr>

Contains a row of calendar cells.

TableHeader<th>

Displays a weekday heading.

PropTypeDescription
dayWeekDay (required)Weekday from the date picker API.
indexnumber (required)Weekday index from api.weekDays, used for its accessible label.

TableBody<tbody>

Contains date rows.

TableCell<td>

Wraps a day button. Pass its Date as value to apply day-cell attributes.

PropTypeDescription
valueDateDate from api.weeks. Omit value when wrapping month or year buttons.

TableCellTrigger<button>

Selects a day, month, or year cell.

PropTypeDescription
valueDate | number (required)Use a Date from api.weeks for a day, a zero-based month from api.months for a month, or a year from api.years for a year.
AttributeValuePurpose
data-selectedPresent when selectedStyles a selected date.
data-range-startPresent at the range startStyles the range start.
data-range-endPresent at the range endStyles the range end.
data-in-rangePresent inside the rangeStyles the active endpoint preview interval.

Status<div>

Announces calendar status text in a visually hidden region.

Accessibility

Provide a visible label and format hint. A typed date must be a real calendar date within Root's min and max; an empty optional input remains valid.