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.jsonpnpm dlx shadcn@latest add https://oui.open.gov.sg/r/date-field.jsonnpx shadcn@latest add https://oui.open.gov.sg/r/date-field.jsonbunx --bun shadcn@latest add https://oui.open.gov.sg/r/date-field.jsonDateField 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/dateimport { 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/dateimport { 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/dateimport { 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.errorMessageaccepts 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 newDateValue(ornull).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-labelledbyandaria-describedby. - If no visible label is provided, pass
aria-labeloraria-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
| Prop | Type | Default | Description |
|---|---|---|---|
label | string | - | The visible label for the field |
description | string | - | Helper text shown below the field |
errorMessage | string | ((validation: ValidationResult) => string) | - | Error message displayed when the field is invalid |
value | DateValue | null | - | The current date value (controlled) |
defaultValue | DateValue | null | - | The default date value (uncontrolled) |
onChange | (value: DateValue | null) => void | - | Called when the date value changes |
isDisabled | boolean | false | Whether the field is disabled |
isReadOnly | boolean | false | Whether the field is read-only |
isRequired | boolean | false | Whether the field is required |
isInvalid | boolean | false | Whether 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 |
minValue | DateValue | - | The minimum accepted date |
maxValue | DateValue | - | The maximum accepted date |
size | "xs" | "sm" | "md" | "md" | The size of the field |
variant | "outline" | "unstyled" | "outline" | The visual variant of the input |
classNames | SlotsToClasses<"base" | "label" | "input" | "description" | "error"> | - | Custom Tailwind classes for each field slot |
inputProps | Partial<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.