---
title: Date picker (React) - USWDS + Tailwind
description: Let people enter or choose a single date with a date picker and
  calendar controls.
---

# Date picker

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

## Examples

```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.

| Prop | Type | Description |
| - | - | - |
| `selectionMode` | `single \| range` | Selects one date or a date range. |
| `min` | `Date` | Earliest selectable calendar date. |
| `max` | `Date` | Latest 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. |
| `disabled` | `boolean` | Disables date selection. |
| `onValueChange` | `({ value: (Date \| undefined)[]; valueAsString: string[]; view: day \| month \| year }) => void` | Receives 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 }) => void` | Receives calendar visibility changes. |
| `ids` | `Datepicker ElementIds` | Overrides generated part IDs. Input, label, and trigger IDs use callbacks, such as input: index => index === 0 ? "start-date" : "end-date". |
| `defaultDate` | `Date` | Initial navigation fallback when no date is selected. Does not select or submit a value. |
| `rangeAnchor` | `Date` | Fixed peer date for range markers and preview; independent of the selected value. |
| `readOnly` | `boolean` | Makes the visible inputs read-only and prevents calendar editing. |
| `required` | `boolean` | Marks the visible inputs required. |
| `locale` | `string` | Locale for month and weekday labels. Defaults to English. |

| Attribute | Value | Purpose |
| - | - | - |
| `data-state` | `open \| closed` | Style the open and closed states. |
| `data-disabled` | `Present when disabled` | Style 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.

| Prop | Type | Description |
| - | - | - |
| `index` | `0 \| 1` | Endpoint 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.

| Prop | Type | Description |
| - | - | - |
| `index` | `0 \| 1` | Endpoint to edit or open: 0 for start, 1 for end. Use the same index on each input and its trigger. |

| Attribute | Value | Purpose |
| - | - | - |
| `data-state` | `open \| closed` | Style the open and closed states. |

### Content `<div>`

Place the calendar views inside the popover.

| Attribute | Value | Purpose |
| - | - | - |
| `data-state` | `open \| closed` | Style 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.

| Prop | Type | Description |
| - | - | - |
| `view` | `day \| year` | Navigation view. Defaults to day. |
| `unit` | `month \| year` | Day-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.

| Prop | Type | Description |
| - | - | - |
| `view` | `day \| year` | Navigation view. Defaults to day. |
| `unit` | `month \| year` | Day-view navigation step. Defaults to month; omit in year view. |

### ViewTrigger `<button>`

Opens month or year selection.

| Prop | Type | Description |
| - | - | - |
| `view` | `month \| year (required)` | View to open. |

### View `<div>`

Renders the day, month, or year view.

| Prop | Type | Description |
| - | - | - |
| `view` | `day \| month \| year (required)` | Selects the displayed calendar view. |
| `children` | `ReactNode \| ({ api }) => ReactNode` | Can 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.

| Prop | Type | Description |
| - | - | - |
| `day` | `WeekDay (required)` | Weekday from the date picker API. |
| `index` | `number (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.

| Prop | Type | Description |
| - | - | - |
| `value` | `Date` | Date from api.weeks. Omit value when wrapping month or year buttons. |

### TableCellTrigger `<button>`

Selects a day, month, or year cell.

| Prop | Type | Description |
| - | - | - |
| `value` | `Date \| 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. |

| Attribute | Value | Purpose |
| - | - | - |
| `data-selected` | `Present when selected` | Styles a selected date. |
| `data-range-start` | `Present at the range start` | Styles the range start. |
| `data-range-end` | `Present at the range end` | Styles the range end. |
| `data-in-range` | `Present inside the range` | Styles 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.
