EnterUI
DocsComponentsThemesExamplesDonation
Get Started
    • DatePicker
    • DateRangePicker
    • DatePresetPicker

Default usage

  1. EnterUI
  2. /Docs
  3. /Scheduling
  4. /Date Pickers
  5. /DatePicker
Updated 13 October 2026

DatePicker

A polished calendar-driven date picker with month navigation.

DatePicker defines how EnterUI handles a polished calendar-driven date picker with month navigation, including default structure, accessible states, and token-driven styling.

#Default usage

Default

Click the trigger to open a styled calendar popover.

Code
<FormField label="Release date" labelProps={{ htmlFor: "datepicker-default" }}>
  <DatePicker id="datepicker-default" />
</FormField>

#Import

Code
import { DateInput, DatePicker, FormField } from "@froggdesign/enter-ui-react";

#Patterns

Helper text

Pair DatePicker with FormField description for hints.

Used for visible release notes.

Invalid

Pass invalid to surface field-level error styling.

Pick a date in the future.

Disabled

Disabled DatePicker stays readable but inert.

#Check-in and check-out

Use two controlled DatePickers with a shared range to reproduce the FroggTravel search form. Open either field to see both endpoints and the dates between them highlighted. Check-in cannot be in the past; check-out allows 1 to 30 nights. Changing check-in clears check-out when the existing stay no longer fits. Clearing check-in also clears and disables check-out until a new check-in is selected.

Travel check-in / check-out

Two linked calendars with shared range highlighting and a valid stay window. Try changing or clearing check-in.

Choose a stay of 1 to 30 nights.

Code
"use client";

import { DatePicker, FormField, Grid } from "@froggdesign/enter-ui-react";
import { useId, useState } from "react";

function addDays(isoDate: string, days: number) {
  const date = new Date(`${isoDate}T12:00:00Z`);
  date.setUTCDate(date.getUTCDate() + days);
  return date.toISOString().slice(0, 10);
}

export function TravelDatePickerExample() {
  const id = useId();
  const [today] = useState(() => new Date().toISOString().slice(0, 10));
  const [start, setStart] = useState(() => addDays(today, 14));
  const [end, setEnd] = useState(() => addDays(today, 19));
  const range = { start, end };

  function changeStart(next: string) {
    setStart(next);
    if (!next || (end && (end <= next || end > addDays(next, 30)))) {
      setEnd("");
    }
  }

  return (
    <Grid min={220} gap={16} alignItems="start">
      <FormField label="Check-in" labelProps={{ htmlFor: `${id}-in` }}>
        <DatePicker
          id={`${id}-in`}
          locale="en-GB"
          min={today}
          value={start}
          onValueChange={changeStart}
          range={range}
          placeholder="Select check-in"
        />
      </FormField>
      <FormField
        label="Check-out"
        labelProps={{ htmlFor: `${id}-out` }}
        description="Choose a stay of 1 to 30 nights."
      >
        <DatePicker
          id={`${id}-out`}
          locale="en-GB"
          min={start ? addDays(start, 1) : today}
          max={start ? addDays(start, 30) : undefined}
          disabled={!start}
          value={end}
          onValueChange={setEnd}
          range={range}
          placeholder="Select check-out"
        />
      </FormField>
    </Grid>
  );
}

#Bounded dates

Travel dates

Choose a date within a stay window. Unavailable days and months cannot be selected.

Choose a stay of 1 to 30 nights.

Code
<DatePicker
  value={checkOut}
  onValueChange={setCheckOut}
  min={firstAvailableCheckout}
  max={lastAvailableCheckout}
  locale="en-GB"
  placeholder="Select check-out"
/>

DatePicker owns the calendar popover; use it instead of Input type="date" when a consistent EnterUI calendar is required. Travel forms clear an incompatible check-out after check-in changes rather than silently choosing a different stay.

#Accessibility

DatePicker uses a button trigger that opens a custom calendar popover. The trigger surfaces the selected date via its visible label, supports keyboard activation, and accepts aria-label, aria-labelledby, and aria-describedby. Each calendar day renders as a button with a full date label for screen readers. Pair with Label or FormField for a labeled context.

Opening focuses the selected date, or an allowed date nearest today when empty. Only one date is in the Tab sequence. Left/Right move one day, Up/Down one week, Home/End to week boundaries, and Page Up/Down one month. Hold Shift with Page Up/Down to move one year. Movement respects min and max; Enter or Space selects, and Escape closes without changing the value. Selection returns focus to the trigger. Today is identified with aria-current="date".

The popover stays within the viewport on narrow screens. The popover scrolls on short screens so every date and footer action remains accessible. Day targets use a seven-column layout with readable labels; month controls are disabled when that month contains no selectable dates. Impossible ISO dates such as 2026-02-31 display the placeholder.

#API

  • value / defaultValue: ISO date string (YYYY-MM-DD).
  • onValueChange(value): fires when the user selects or clears a date.
  • min / max: ISO date bounds.
  • range={{ start, end }}: inclusive ISO date range highlighted in the calendar; bounds and selection remain controlled by each picker.
  • invalid sets aria-invalid and applies the error border / focus ring.
  • size: sm, md (default), or lg.
  • weekStartsOn: 0 (Sunday) or 1 (Monday, default).
  • locale: BCP 47 locale string for month, weekday, and date labels.
  • name: when set, renders a hidden input for form submission.
  • DateInput is exported as an alias.

#Styling

The date trigger uses the same medium (8px) radius as text and selection controls; the calendar popover retains its separate surface radius.

Every exported component accepts className when it renders an element. EnterUI styles use .eui-* classes and --eui-* variables mapped from FroggDesign theme tokens.

#Linked date ranges

Pass the same range={{ start, end }} to both check-in and check-out pickers. The inclusive range is highlighted green in either calendar, including dates in adjacent months. Endpoints use a stronger green. The range only adds visual context: each picker keeps its own value, callback, and min / max bounds. Incomplete, invalid, or reversed ranges are not highlighted.

Code
<DatePicker value={start} onValueChange={setStart} range={{ start, end }} />
<DatePicker value={end} onValueChange={setEnd} range={{ start, end }} min={start} />
PreviousTimeInput
NextDateRangePicker
On this page
  • Default usage
  • Import
  • Patterns
  • Check-in and check-out
  • Bounded dates
  • Accessibility
  • API
  • Styling
  • Linked date ranges