---
title: Date picker (Vanilla) - 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.

## Example

```html
<div
  data-scope="date-picker" data-part="root"
  id="date-picker-1"
>
  <div class="max-w-xs mb-2">
    <label data-part="label"
      class="block"
      for="date-picker:date-picker-1:input:0"
      id="appointment-date-label"
    >Appointment date</label>
    <div id="appointment-date-hint" class="text-gray-50">mm/dd/yyyy</div>
  </div>
  <div class="flex max-w-mobile-lg relative">
    <input
      data-part="input"
      id="date-picker:date-picker-1:input:0"
      name="appointmentDate"
      type="text"
      placeholder="MM/DD/YYYY"
      class="p-2 w-full h-10 border border-gray-60 focus:outline-offset-0 focus:outline-4 focus:outline-blue-40v data-[invalid]:ring-4 placeholder:invisible"
      aria-describedby="appointment-date-hint"
    />
    <button
      data-part="trigger"
      class="cursor-pointer w-12 bg-transparent hover:bg-gray-10! data-[state=open]:bg-gray-5 active:bg-gray-30 focus:outline-4 focus:outline-blue-40v focus:-outline-offset-4 flex items-center justify-center"
      aria-label="Toggle calendar"
    >
      <span class="icon-[material-symbols--calendar-today] size-7"></span>
    </button>
    <div
      data-part="content"
      class="bg-gray-5 w-mobile absolute top-10 right-0 not-data-[state=open]:hidden"
    >
      <div data-part="view" data-view="day">
        <div data-part="view-control" class="flex w-full justify-between">
          <div class="flex">
            <button
              data-part="prev-trigger"
              data-unit="year"
              class="size-10 flex items-center justify-center hover:bg-gray-10 cursor-pointer focus:outline-4 focus:outline-blue-40v focus:-outline-offset-4 disabled:cursor-not-allowed disabled:opacity-0"
            >
              <span class="icon-[material-symbols--keyboard-double-arrow-left] size-6"></span>
            </button>
            <button
              data-part="prev-trigger"
              data-unit="month"
              class="size-10 flex items-center justify-center hover:bg-gray-10 cursor-pointer focus:outline-4 focus:outline-blue-40v focus:-outline-offset-4 disabled:cursor-not-allowed disabled:opacity-0"
            >
              <span class="icon-[material-symbols--keyboard-arrow-left] size-6"></span>
            </button>
          </div>
          <div class="flex grow-1 justify-center">
            <button
              data-part="view-trigger"
              data-view="month"
              class="h-10 px-1 flex items-center hover:bg-gray-10 cursor-pointer focus:outline-4 focus:outline-blue-40v focus:-outline-offset-4"
            ></button>
            <button
              data-part="view-trigger"
              data-view="year"
              class="h-10 px-1 flex items-center hover:bg-gray-10 cursor-pointer focus:outline-4 focus:outline-blue-40v focus:-outline-offset-4"
            ></button>
          </div>
          <div class="flex">
            <button
              data-part="next-trigger"
              data-unit="month"
              class="size-10 flex items-center justify-center hover:bg-gray-10 cursor-pointer focus:outline-4 focus:outline-blue-40v focus:-outline-offset-4 disabled:cursor-not-allowed disabled:opacity-0"
            >
              <span class="icon-[material-symbols--keyboard-arrow-right] size-6"></span>
            </button>
            <button
              data-part="next-trigger"
              data-unit="year"
              class="size-10 flex items-center justify-center hover:bg-gray-10 cursor-pointer focus:outline-4 focus:outline-blue-40v focus:-outline-offset-4 disabled:cursor-not-allowed disabled:opacity-0"
            >
              <span class="icon-[material-symbols--keyboard-double-arrow-right] size-6"></span>
            </button>
          </div>
        </div>
        <table class="w-full">
          <thead>
            <tr>
              <th
                data-part="table-header"
                class="text-center py-1.5 font-normal"
              ></th>
            </tr>
          </thead>
          <tbody>
            <tr>
              <td>
                <button
                  data-part="table-cell-trigger"
                  class="w-full py-1.5 hover:bg-gray-10 cursor-pointer text-center data-previous-month:text-gray-warm-60 data-next-month:text-gray-warm-60 data-[focus]:outline-2 data-[focus]:outline-blue-warm-80v data-[focus]:-outline-offset-2 focus:outline-4! focus:outline-blue-40v! focus:-outline-offset-4! data-[range-start]:bg-blue-warm-60v data-[range-start]:text-white data-[range-start]:rounded-s-sm data-[range-end]:bg-blue-warm-60v data-[range-end]:text-white data-[range-end]:rounded-e-sm data-[in-range]:bg-blue-warm-10v data-[in-range]:text-ink data-[range-hover]:bg-blue-warm-10v active:bg-gray-30 data-selected:active:bg-blue-warm-70v disabled:cursor-not-allowed disabled:opacity-60 disabled:text-black/30 disabled:hover:bg-transparent"
                ></button>
              </td>
            </tr>
          </tbody>
        </table>
      </div>
      <div
        data-part="view" data-view="month"
        class="py-5 px-1.5"
      >
        <table class="w-full">
          <tbody>
            <tr>
              <td class="w-1/3">
                <button
                  data-part="table-cell-trigger"
                  class="size-full text-center py-2.5 hover:bg-gray-10 cursor-pointer data-[focus]:outline-2 data-[focus]:outline-blue-warm-80v data-[focus]:-outline-offset-2 data-[selected]:bg-blue-warm-60v data-[selected]:text-white focus:outline-4! focus:outline-blue-40v! focus:-outline-offset-4! leading-none disabled:cursor-not-allowed disabled:opacity-60 disabled:text-black/30 disabled:hover:bg-transparent"
                ></button>
              </td>
            </tr>
          </tbody>
        </table>
      </div>
      <div
        data-part="view" data-view="year"
        class="py-5 px-1.5"
      >
        <div class="flex items-center">
          <button
            data-part="prev-trigger"
            data-view="year"
            class="hover:bg-gray-10 cursor-pointer text-center h-26 w-16 shrink-0"
          >
            <span class="icon-[material-symbols--keyboard-arrow-left] size-8"></span>
          </button>
          <table class="w-full">
            <tbody>
              <tr>
                <td class="w-1/3">
                  <button
                    data-part="table-cell-trigger"
                    class="size-full text-center py-2.5 hover:bg-gray-10 cursor-pointer data-[focus]:outline-2 data-[focus]:outline-blue-warm-80v data-[focus]:-outline-offset-2 data-[selected]:bg-blue-warm-60v data-[selected]:text-white focus:outline-4! focus:outline-blue-40v! focus:-outline-offset-4! leading-none disabled:cursor-not-allowed disabled:opacity-60 disabled:text-black/30 disabled:hover:bg-transparent"
                  ></button>
                </td>
              </tr>
            </tbody>
          </table>
          <button
            data-part="next-trigger"
            data-view="year"
            class="hover:bg-gray-10 cursor-pointer text-center h-26 w-16 shrink-0"
          >
            <span class="icon-[material-symbols--keyboard-arrow-right] size-8"></span>
          </button>
        </div>
      </div>
    </div>
  </div>
  <div data-part="status" class="sr-only"></div>
</div>
```

## Component API

### Initialization

```js
import { datePickerInit } from '@uswds-tailwind/compat/date-picker'

datePickerInit()
```

The initializer returns an array of instances and reuses existing instances. For one picker, use `DatePicker.getOrCreateInstance(elementOrSelector, options)`.

### Anatomy

Use `data-scope="date-picker" data-part="root"` on the root. Descendant parts use short names such as `input`, `trigger`, `content`, and `status`. Keep all three `view` elements, with `data-view="day"`, `"month"`, and `"year"`, and the complete table templates from the example.

The day view needs `view-control`, `prev-trigger` and `next-trigger` buttons with `data-unit="month"` or `"year"`, and `view-trigger` buttons with `data-view="month"` or `"year"`. The year view uses `prev-trigger` and `next-trigger` with `data-view="year"` to move through twelve-year ranges. Each view needs a `table-cell-trigger` button template. The day table also needs a `table-header` template.

The runtime generates calendar rows, IDs, labels, visibility, and state attributes. Include a `status` region for announcements.

### Configuration

Constructor options take precedence over authored HTML. Attributes are read during construction; later attribute changes do not reconfigure the instance.

| HTML source | Constructor option | Behavior |
| - | - | - |
| `data-min-date`, or input `min` | `min: Date` | Earliest selectable date. HTML uses ISO `YYYY-MM-DD`. |
| `data-max-date`, or input `max` | `max: Date` | Latest selectable date. HTML uses ISO `YYYY-MM-DD`. |
| `data-default-value` | `defaultValue: (Date \| undefined)[]` | Initial selection. HTML uses an ISO date; constructor values use Date objects. |
| `data-default-date` | `defaultDate: Date` | Navigation fallback when no date is selected. Does not select a value. |
| `data-range-date` | `rangeAnchor: Date` | Fixed peer date for range markers and preview. |
| Root or input `disabled` | `disabled: boolean` | Disables the picker. |
| Root or input `readonly` | `readOnly: boolean` | Prevents input and calendar editing. |
| Input `name`, `required` | `name: string`, `required: boolean` | Native form name and required state. |
| `<html lang>` | `locale: string` | Locale for month and weekday labels. Uses English if `<html lang>` is unset. |

Use `new Date(2026, 8, 28)` for a local September 28 date in constructor options. The visible input uses `MM/DD/YYYY`. Its authored `value` does not initialize the single picker’s selection; use `data-default-value` or `defaultValue`.

### Accessibility and lifecycle

Provide a visible label, a format hint, a named calendar trigger, and a status region. Typed dates must be real calendar dates within the bounds. Empty optional fields remain valid; blur and Enter update native validity.

Keep the complete calendar templates. Retain the root element because initialization changes generated IDs, and call `destroy()` before removing its markup. See [instance lifecycle](https://uswds-tailwind.com/docs/vanilla/javascript#instance-lifecycle).
