Skip to main content

Date picker

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

Example

Breakpoints

Mobile

320px

Mobile Lg

480px

Tablet

640px

Desktop

1024px
mm/dd/yyyy
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

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.