Open UI

DateField

A date field allows users to enter and edit date and time values using a keyboard. Each part of a date value is displayed in an individually editable segment.

import { CalendarDate } from "@internationalized/date"import { DateField } from "@opengovsg/oui"export const Example = () => {  return (    <DateField      label="Birth date"      placeholderValue={new CalendarDate(2019, 7, 27)}    />  )}

Usage

Use DateField for keyboard date entry without a calendar UI. Use DatePicker when users benefit from picking from a calendar. Use DateRangePicker for ranges.

import { DateField } from "@opengovsg/oui"
<DateField label="Start date" />

Alternatively, install the component as local source via the shadcn CLI:

npx shadcn@latest add https://oui.open.gov.sg/r/date-field.json
pnpm dlx shadcn@latest add https://oui.open.gov.sg/r/date-field.json
npx shadcn@latest add https://oui.open.gov.sg/r/date-field.json
bunx --bun shadcn@latest add https://oui.open.gov.sg/r/date-field.json

DateField renders individual editable segments for each part of a date value (year, month, day), built on React Aria's DateField. Date values use objects from the @internationalized/date package.

If no visible label is provided, an aria-label or aria-labelledby prop must be passed to identify the field to assistive technology.

Examples

Sizes

Use the size prop to change the size of the field. Defaults to "md"

import {  parseAbsoluteToLocal,  parseZonedDateTime,} from "@internationalized/date"import { DateField } from "@opengovsg/oui"export const Example = () => {  return (    <div className="flex w-full flex-col gap-4">      <DateField        size="xs"        defaultValue={parseZonedDateTime(          "2025-11-04T03:45[America/Los_Angeles]",        )}        label="Event date (xs)"      />      <DateField        size="sm"        defaultValue={parseAbsoluteToLocal("2025-11-04T03:45Z")}        label="Event date (sm)"      />      <DateField        size="md"        defaultValue={parseZonedDateTime("2025-11-04T03:45[Asia/Tokyo]")}        label="Event date (md)"      />    </div>  )}

Disabled

import { CalendarDate } from "@internationalized/date"import { DateField } from "@opengovsg/oui"export const Example = () => {  return (    <DateField      isDisabled      label="Birth date"      placeholderValue={new CalendarDate(2019, 7, 27)}    />  )}

Read Only

import { CalendarDate } from "@internationalized/date"import { DateField } from "@opengovsg/oui"export const Example = () => {  return (    <DateField      isReadOnly      label="Birth date"      defaultValue={new CalendarDate(2019, 7, 27)}    />  )}

With Description

You can add a description to the input by passing the description property.

import { CalendarDate } from "@internationalized/date"import { DateField } from "@opengovsg/oui"export const Example = () => {  return (    <DateField      label="Birth date"      placeholderValue={new CalendarDate(2019, 7, 27)}      description="Please enter your birth date."    />  )}

With Error Message

You can combine the isInvalid and errorMessage properties to show an invalid input.

import { DateField } from "@opengovsg/oui"export const Example = () => {  return (    <DateField      label="Birth date"      isInvalid      errorMessage="Please enter a valid date"    />  )}

Controlled

You can use the value and onChange properties to control the input value.

import type { CalendarDate } from "@internationalized/date"import { useState } from "react"import { getLocalTimeZone, parseDate } from "@internationalized/date"import { useDateFormatter } from "@react-aria/i18n"import { DateField } from "@opengovsg/oui"export const Example = () => {  const [value, setValue] = useState<CalendarDate | null>(    parseDate("2024-04-04"),  )  const formatter = useDateFormatter({ dateStyle: "full" })  return (    <div className="flex w-full flex-row gap-2">      <div className="flex w-full flex-col gap-y-2">        <DateField          label="Date (controlled)"          value={value}          onChange={setValue}        />        <p className="text-default-500 text-sm">          Selected date:{" "}          {value ? formatter.format(value.toDate(getLocalTimeZone())) : "--"}        </p>      </div>    </div>  )}

Time Zones

DateField is timezone aware when a ZonedDateTime object is provided as the value. In this case, the time zone abbreviation is displayed, and time zone concerns such as daylight saving time are taken into account when the value is manipulated.

@internationalized/date includes functions for parsing strings in multiple formats into ZonedDateTime objects.

npm install @internationalized/date
import { parseZonedDateTime } from "@internationalized/date"
import {  parseAbsoluteToLocal,  parseZonedDateTime,} from "@internationalized/date"import { DateField } from "@opengovsg/oui"export const Example = () => {  return (    <div className="flex w-full flex-col gap-4">      <DateField        defaultValue={parseZonedDateTime(          "2025-11-04T03:45[America/Los_Angeles]",        )}        label="Event date"      />      <DateField        defaultValue={parseAbsoluteToLocal("2025-11-04T03:45Z")}        label="Event date"      />    </div>  )}

Min Date And Max Date

The minValue and maxValue props can also be used to ensure the value is within a specific range.

@internationalized/date includes functions for parsing strings in multiple formats into ZonedDateTime objects.

npm install @internationalized/date
import { getLocalTimeZone, parseDate, today } from "@internationalized/date"
import { getLocalTimeZone, today } from "@internationalized/date"import { Form } from "react-aria-components"import { Button, DateField } from "@opengovsg/oui"export const Example = () => {  return (    <Form className="flex w-full flex-col gap-4">      <DateField        defaultValue={today(getLocalTimeZone()).subtract({ days: 1 })}        minValue={today(getLocalTimeZone())}        label="Min date"      />      <DateField        defaultValue={today(getLocalTimeZone()).add({ days: 1 })}        label="Max date"        maxValue={today(getLocalTimeZone())}      />      <Button className="w-fit" type="submit">        Submit      </Button>    </Form>  )}

Hide Time Zone

When a ZonedDateTime object is provided as the value to DateField, the time zone abbreviation is displayed by default. However, if this is displayed elsewhere or implicit based on the usecase, it can be hidden using the hideTimeZone option.

@internationalized/date includes functions for parsing strings in multiple formats into ZonedDateTime objects.

npm install @internationalized/date
import { parseZonedDateTime } from "@internationalized/date"
import { parseZonedDateTime } from "@internationalized/date"import { DateField } from "@opengovsg/oui"export const Example = () => {  return (    <DateField      hideTimeZone      defaultValue={parseZonedDateTime("2022-11-07T00:45[America/Los_Angeles]")}      label="Appointment time"    />  )}

Hourly Cycle

By default, DateField displays times in either 12 or 24 hour hour format depending on the user's locale. However, this can be overridden using the hourCycle prop if needed for a specific usecase. This example forces DateField to use 24-hour time, regardless of the locale.

@internationalized/date includes functions for parsing strings in multiple formats into ZonedDateTime objects.

import { parseZonedDateTime } from "@internationalized/date"import { DateField } from "@opengovsg/oui"export const Example = () => {  return (    <DateField      defaultValue={parseZonedDateTime("2025-11-04T15:55[Asia/Singapore]")}      granularity="minute"      hourCycle={24}      label="Appointment time"    />  )}

Validation

See the Forms guide for end-to-end patterns including React Hook Form + Zod.

  • isRequired — Marks the field as required; validation fires on form submit (native) or immediately (aria).
  • validate — A custom function (value: DateValue | null) => ValidationError | true | null | undefined. Return a string to show as the error.
  • isInvalid + errorMessage — Use together for realtime validation feedback. errorMessage accepts a string or a function (validation: ValidationResult) => string.
  • minValue / maxValue — Constrain the accepted date range; entering a date outside the range marks the field as invalid.
  • validationBehavior"native" (default) blocks form submission when invalid; "aria" marks the field via ARIA attributes only (needed for React Hook Form).

Events

  • onChange — Called when the date value changes, receiving the new DateValue (or null).
  • onFocus / onBlur — Called when the field receives or loses focus.
  • onFocusChange — Called when the focus state changes, receiving a boolean.
  • onKeyDown / onKeyUp — Called on keyboard events.

Accessibility

  • Each date segment is a focusable <div role="spinbutton"> — assistive technologies announce the segment name and current value.
  • Navigating between segments uses Tab / Shift+Tab; digits increment the focused segment; Arrow Up / Down also increment or decrement.
  • The label, description, and error message are linked via aria-labelledby and aria-describedby.
  • If no visible label is provided, pass aria-label or aria-labelledby.

Slots

Slots are named regions of the component you can target with custom Tailwind classes via the classNames prop. Each slot below corresponds to a key on the classNames object.

  • base: Input wrapper, it handles alignment, placement, and general appearance.
  • label: Label of the date-input, it is the one that is displayed above, inside or left of the date-input.
  • input: The date-input element.
  • description: The description of the date-input.
  • error: The error message of the date-input.

Props

DateField

PropTypeDefaultDescription
labelstring-The visible label for the field
descriptionstring-Helper text shown below the field
errorMessagestring | ((validation: ValidationResult) => string)-Error message displayed when the field is invalid
valueDateValue | null-The current date value (controlled)
defaultValueDateValue | null-The default date value (uncontrolled)
onChange(value: DateValue | null) => void-Called when the date value changes
isDisabledbooleanfalseWhether the field is disabled
isReadOnlybooleanfalseWhether the field is read-only
isRequiredbooleanfalseWhether the field is required
isInvalidbooleanfalseWhether the field should display as invalid
validate(value: DateValue | null) => ValidationError | true | null | undefined-Custom validation function
validationBehavior"native" | "aria""native"Whether to use native HTML form validation or ARIA-only
minValueDateValue-The minimum accepted date
maxValueDateValue-The maximum accepted date
size"xs" | "sm" | "md""md"The size of the field
variant"outline" | "unstyled""outline"The visual variant of the input
classNamesSlotsToClasses<"base" | "label" | "input" | "description" | "error">-Custom Tailwind classes for each field slot
inputPropsPartial<DateInputProps>-Additional props spread to the underlying DateInput element (escape hatch)

Inherits all remaining props from React Aria's DateField. Notable ones: autoFocus, granularity, hideTimeZone, hourCycle, locale, placeholderValue.

On this page