{"slug":"date-input","title":"Date Input","description":"Using the date-input machine in your project.","contentType":"component","framework":"react","content":"A date input lets you enter a date by typing into segmented input fields (month, day,\nyear, etc.) with validation and keyboard navigation.\n\n> **Good to know**: The date input is built on top of the\n> [`@internationalized/date`](https://react-spectrum.adobe.com/internationalized/date/CalendarDate.html)\n> library.\n\n## Resources\n\n\n[Latest version: v1.39.1](https://www.npmjs.com/package/@zag-js/date-input)\n[Logic Visualizer](https://zag-visualizer.vercel.app/date-input)\n[Source Code](https://github.com/chakra-ui/zag/tree/main/packages/machines/date-input)\n\n\n\n**Features**\n\n- Segmented input fields for each date part (month, day, year, hour, minute, second)\n- Supports `single` and `range` selection modes\n- Keyboard navigation and auto-advance between segments\n- Placeholder management with visual distinction\n- Customizable granularity (day, month, hour, minute, second)\n- Optional leading zeros in numeric fields\n- Works with localization and timezone\n- Full accessibility with keyboard and screen reader support\n- Form integration with hidden input element\n\n## Installation\n\nInstall the date-input package:\n\n```bash\nnpm install @zag-js/date-input @zag-js/react\n# or\nyarn add @zag-js/date-input @zag-js/react\n```\n\n## Anatomy\n\nCheck the date-input anatomy and part names.\n\n> Each part includes a `data-part` attribute to help identify them in the DOM.\n\n\n\n## Usage\n\nImport the package:\n\n```tsx\nimport * as dateInput from \"@zag-js/date-input\"\n```\n\nThese are the key exports:\n\n- `machine` - State machine logic.\n- `connect` - Maps machine state to JSX props and event handlers.\n- `parse` - Parses an ISO 8601 date string.\n\n> You'll also need to provide a unique `id` to the `useMachine` hook. This is\n> used to ensure that every part has a unique identifier.\n\nThen use the framework integration helpers:\n\n```tsx\nimport * as dateInput from \"@zag-js/date-input\"\nimport { useMachine, normalizeProps } from \"@zag-js/react\"\nimport { useId } from \"react\"\n\nfunction DateInput() {\n  const service = useMachine(dateInput.machine, { id: useId() })\n\n  const api = dateInput.connect(service, normalizeProps)\n\n  return (\n    <div {...api.getRootProps()}>\n      <label {...api.getLabelProps()}>Enter a date</label>\n\n      <div {...api.getControlProps()}>\n        <div {...api.getSegmentGroupProps()}>\n          {api.getSegments().map((segment, i) => (\n            <span key={i} {...api.getSegmentProps({ segment })}>\n              {segment.text}\n            </span>\n          ))}\n        </div>\n      </div>\n\n      <input {...api.getHiddenInputProps()} />\n    </div>\n  )\n}\n```\n\n### Rendering segments\n\nUse `api.getSegments()` to get the list of date segments. Each segment has a\n`type` (e.g., `\"month\"`, `\"day\"`, `\"year\"`, `\"literal\"`) and a `text` property\nfor display.\n\n```tsx\n<div {...api.getControlProps()}>\n  <div {...api.getSegmentGroupProps()}>\n    {api.getSegments().map((segment, i) => (\n      <span key={i} {...api.getSegmentProps({ segment })}>\n        {segment.text}\n      </span>\n    ))}\n  </div>\n</div>\n```\n\nFor range mode, pass the `index` to distinguish start and end date segments:\n\n```tsx\n<div {...api.getControlProps()}>\n  <div {...api.getSegmentGroupProps({ index: 0 })}>\n    {api.getSegments({ index: 0 }).map((segment, i) => (\n      <span key={i} {...api.getSegmentProps({ segment, index: 0 })}>\n        {segment.text}\n      </span>\n    ))}\n  </div>\n\n  <span> – </span>\n\n  <div {...api.getSegmentGroupProps({ index: 1 })}>\n    {api.getSegments({ index: 1 }).map((segment, i) => (\n      <span key={i} {...api.getSegmentProps({ segment, index: 1 })}>\n        {segment.text}\n      </span>\n    ))}\n  </div>\n</div>\n```\n\n### Setting the initial date\n\nTo set the initial value rendered by the date input, set `defaultValue`.\n\n```tsx\nconst service = useMachine(dateInput.machine, {\n  defaultValue: [dateInput.parse(\"2024-01-15\")],\n})\n```\n\n### Controlling the selected date\n\nUse `value` and `onValueChange` to programmatically control the selected date.\n\n```tsx\nconst service = useMachine(dateInput.machine, {\n  value: [dateInput.parse(\"2024-01-15\")],\n  onValueChange(details) {\n    // details => { value: DateValue[], valueAsString: string[] }\n    console.log(\"selected date:\", details.valueAsString)\n  },\n})\n```\n\nYou can also set it with `api.setValue`.\n\n```tsx\nconst nextValue = dateInput.parse(\"2024-01-15\")\napi.setValue([nextValue])\n```\n\n### Setting the min and max dates\n\nTo constrain the dates that can be entered, set the `min` and `max` properties.\n\n```tsx\nconst service = useMachine(dateInput.machine, {\n  min: dateInput.parse(\"2024-01-01\"),\n  max: dateInput.parse(\"2024-12-31\"),\n})\n```\n\nWhen typing values outside the range, invalid segments will be marked as invalid.\n\n### Disabling the date input\n\nSet `disabled` to `true` to make the input non-interactive.\n\n```tsx\nconst service = useMachine(dateInput.machine, {\n  disabled: true,\n})\n```\n\n### Setting read-only mode\n\nSet `readOnly` to `true` to prevent value changes while allowing focus.\n\n```tsx\nconst service = useMachine(dateInput.machine, {\n  readOnly: true,\n})\n```\n\n### Required and invalid state\n\nUse `required` and `invalid` for form validation and UI state.\n\n```tsx\nconst service = useMachine(dateInput.machine, {\n  required: true,\n  invalid: false,\n})\n```\n\n### Controlling date granularity\n\nSet `granularity` to control the smallest unit displayed in the input.\n\n```tsx\nconst service = useMachine(dateInput.machine, {\n  granularity: \"day\", // \"month\" | \"day\" | \"year\" | \"hour\" | \"minute\" | \"second\"\n})\n```\n\nThe available granularities are: `\"month\"`, `\"day\"`, `\"year\"`, `\"hour\"`, `\"minute\"`, and\n`\"second\"`.\n\n### Forcing leading zeros\n\nSet `shouldForceLeadingZeros` to always display leading zeros in numeric fields\n(e.g., \"01\" instead of \"1\").\n\n```tsx\nconst service = useMachine(dateInput.machine, {\n  shouldForceLeadingZeros: true,\n})\n```\n\nBy default, leading zeros follow the locale's conventions.\n\n### Controlling the placeholder date\n\nUse `placeholderValue` to set the initial placeholder date, which is shown in\nunfilled segments.\n\n```tsx\nconst service = useMachine(dateInput.machine, {\n  placeholderValue: dateInput.parse(\"2024-01-01\"),\n  defaultPlaceholderValue: dateInput.parse(\"2024-01-01\"),\n})\n```\n\nListen for placeholder changes with `onPlaceholderChange`:\n\n```tsx\nconst service = useMachine(dateInput.machine, {\n  onPlaceholderChange(details) {\n    // details => { placeholderValue: DateValue, value: DateValue[], valueAsString: string[] }\n    console.log(\"placeholder changed:\", details.placeholderValue)\n  },\n})\n```\n\n### Listening to focus changes\n\nUse `onFocusChange` to listen for when the input gains or loses focus.\n\n```tsx\nconst service = useMachine(dateInput.machine, {\n  onFocusChange(details) {\n    // details => { focused: boolean }\n    console.log(\"focused:\", details.focused)\n  },\n})\n```\n\n### Choosing a selection mode\n\nUse `selectionMode` to allow entering a single date or a date range.\n\n```tsx\nconst service = useMachine(dateInput.machine, {\n  selectionMode: \"range\", // \"single\" | \"range\"\n})\n```\n\nIn range mode, the input will have segments for both start and end dates.\n\n### Working with display values\n\nThe `displayValues` property tracks partially entered dates while the user is\ntyping. This is useful for showing the editing state before a complete date is\nentered.\n\n```tsx\nconst displayValues = api.displayValues\n// Each incomplete date shows which segments have been filled in\n```\n\n### Clearing the date\n\nUse `api.clearValue()` to clear the selected date.\n\n```tsx\napi.clearValue()\n```\n\n### Accessing segments\n\nUse `api.getSegments()` to access the individual date segments.\n\n```tsx\nconst segments = api.getSegments()\n\nsegments.forEach((segment) => {\n  console.log(segment.type)      // \"month\", \"day\", \"year\", etc.\n  console.log(segment.text)      // displayed text\n  console.log(segment.value)     // numeric value\n  console.log(segment.isEditable) // whether user can edit\n})\n```\n\nThen render each segment:\n\n```tsx\n<div {...api.getControlProps()}>\n  <div {...api.getSegmentGroupProps()}>\n    {api.getSegments().map((segment, i) => (\n      <span key={i} {...api.getSegmentProps({ segment })}>\n        {segment.text}\n      </span>\n    ))}\n  </div>\n</div>\n```\n\n### Checking segment editability\n\nUse `api.getSegmentState()` to determine if a segment can be edited.\n\n```tsx\nconst state = api.getSegmentState({ segment })\nconsole.log(state.editable) // true if the segment is editable\n```\n\n### Using the hidden input for forms\n\nThe date input includes a hidden input element for form submission.\n\n```tsx\n<input {...api.getHiddenInputProps()} />\n```\n\nSet the `name` attribute to include it in form data:\n\n```tsx\nconst service = useMachine(dateInput.machine, {\n  name: \"birthDate\",\n})\n```\n\nMultiple date inputs (range mode) can use a `name` prop on `getHiddenInputProps`:\n\n```tsx\n<input {...api.getHiddenInputProps({ index: 0, name: \"startDate\" })} />\n<input {...api.getHiddenInputProps({ index: 1, name: \"endDate\" })} />\n```\n\n### Labeling the input\n\nUse `getLabelProps()` to properly label the input for accessibility.\n\n```tsx\n<label {...api.getLabelProps()}>Select a date</label>\n<div {...api.getControlProps()}>\n  {/* segments */}\n</div>\n```\n\n### Setting locale and timezone\n\nSet `locale` and `timeZone` to control date parsing and formatting.\n\n```tsx\nconst service = useMachine(dateInput.machine, {\n  locale: \"en-GB\",\n  timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone,\n})\n```\n\n### Hour cycle (12-hour vs 24-hour time)\n\nSet `hourCycle` to control time format display.\n\n```tsx\nconst service = useMachine(dateInput.machine, {\n  granularity: \"hour\",\n  hourCycle: 24, // 12 | 24\n})\n```\n\nBy default, this is determined by the locale.\n\n### Custom formatter\n\nProvide a `formatter` to customize how dates are parsed and formatted.\n\n```tsx\nimport { DateFormatter } from \"@internationalized/date\"\n\nconst service = useMachine(dateInput.machine, {\n  formatter: new DateFormatter(\"en-US\", {\n    dateStyle: \"short\",\n    timeStyle: \"short\",\n  }),\n})\n```\n\n### Form integration\n\nUse the date input within a form with standard HTML form handling:\n\n```tsx\n<form\n  onSubmit={(e) => {\n    e.preventDefault()\n    const formData = new FormData(e.currentTarget)\n    const date = formData.get(\"birthDate\")\n    console.log(\"selected date:\", date)\n  }}\n>\n  <label {...api.getLabelProps()}>Birth Date</label>\n  <div {...api.getControlProps()}>\n    <div {...api.getSegmentGroupProps()}>\n      {api.getSegments().map((segment, i) => (\n        <span key={i} {...api.getSegmentProps({ segment })}>\n          {segment.text}\n        </span>\n      ))}\n    </div>\n  </div>\n  <input {...api.getHiddenInputProps({ name: \"birthDate\" })} />\n  <button type=\"submit\">Submit</button>\n</form>\n```\n\n### Listening to date changes\n\nUse `onValueChange` to listen for date changes.\n\n```tsx\nconst service = useMachine(dateInput.machine, {\n  onValueChange(details) {\n    // details => { value: DateValue[], valueAsString: string[] }\n    console.log(\"selected date:\", details.valueAsString)\n  },\n})\n```\n\n### Localization\n\nUse `translations` to customize accessibility labels and messages.\n\n```tsx\nconst service = useMachine(dateInput.machine, {\n  translations: {\n    placeholder: (locale) => ({\n      year: \"YYYY\",\n      month: \"MM\",\n      day: \"DD\",\n      hour: \"HH\",\n      minute: \"MM\",\n      second: \"SS\",\n    }),\n  },\n})\n```\n\n## Styling guide\n\nEach date-input part includes a `data-part` attribute you can target in CSS.\n\n```css\n[data-scope=\"date-input\"][data-part=\"root\"] {\n  /* styles for the root container */\n}\n\n[data-scope=\"date-input\"][data-part=\"label\"] {\n  /* styles for the label */\n}\n\n[data-scope=\"date-input\"][data-part=\"control\"] {\n  /* styles for the control container */\n}\n\n[data-scope=\"date-input\"][data-part=\"segment-group\"] {\n  /* styles for the segment group */\n}\n\n[data-scope=\"date-input\"][data-part=\"segment\"] {\n  /* styles for each segment */\n}\n\n[data-scope=\"date-input\"][data-part=\"hidden-input\"] {\n  /* styles for the hidden input */\n}\n```\n\n### State attributes\n\n```css\n[data-scope=\"date-input\"][data-part=\"root\"] {\n  &[data-disabled] {\n    /* styles for disabled state */\n  }\n\n  &[data-readonly] {\n    /* styles for read-only state */\n  }\n\n  &[data-invalid] {\n    /* styles for invalid state */\n  }\n}\n```\n\n### Segment states\n\n```css\n[data-scope=\"date-input\"][data-part=\"segment\"] {\n  &[data-placeholder-shown] {\n    /* styles for placeholder segments */\n  }\n\n  &[data-type=\"month\"] {\n    /* styles for month segments */\n  }\n\n  &[data-type=\"day\"] {\n    /* styles for day segments */\n  }\n\n  &[data-type=\"year\"] {\n    /* styles for year segments */\n  }\n\n  &[data-type=\"hour\"] {\n    /* styles for hour segments */\n  }\n\n  &[data-type=\"minute\"] {\n    /* styles for minute segments */\n  }\n\n  &[data-type=\"second\"] {\n    /* styles for second segments */\n  }\n}\n```\n\n## Methods and Properties\n\n### Machine Context\n\nThe date input machine exposes the following context properties:\n\n**`locale`**\nType: `string`\nDescription: The locale (BCP 47 language tag) to use when formatting the date.\n\n**`createCalendar`**\nType: `(identifier: CalendarIdentifier) => Calendar`\nDescription: A function that creates a calendar object for a given calendar identifier.\nUse this to support non-Gregorian calendars (e.g., Persian, Islamic, Buddhist).\n\n**`translations`**\nType: `IntlTranslations`\nDescription: The localized messages to use.\n\n**`ids`**\nType: `Partial<{ root: string; label: (index: number) => string; control: string; segmentGroup: (index: number) => string; hiddenInput: (index: number) => string; }>`\nDescription: The ids of the elements in the date input. Useful for composition.\n\n**`name`**\nType: `string`\nDescription: The `name` attribute of the input element.\n\n**`form`**\nType: `string`\nDescription: The `form` attribute of the hidden input element.\n\n**`timeZone`**\nType: `string`\nDescription: The time zone to use\n\n**`disabled`**\nType: `boolean`\nDescription: Whether the date input is disabled.\n\n**`readOnly`**\nType: `boolean`\nDescription: Whether the date input is read-only.\n\n**`required`**\nType: `boolean`\nDescription: Whether the date input is required\n\n**`invalid`**\nType: `boolean`\nDescription: Whether the date input is invalid\n\n**`isDateUnavailable`**\nType: `(date: DateValue, locale: string) => boolean`\nDescription: Returns whether a date is unavailable.\nWhen a committed date matches, the input is marked as invalid.\n\n**`min`**\nType: `DateValue`\nDescription: The minimum date that can be selected.\n\n**`max`**\nType: `DateValue`\nDescription: The maximum date that can be selected.\n\n**`value`**\nType: `DateValue[]`\nDescription: The controlled selected date(s).\n\n**`defaultValue`**\nType: `DateValue[]`\nDescription: The initial selected date(s) when rendered.\nUse when you don't need to control the selected date(s).\n\n**`placeholderValue`**\nType: `DateValue`\nDescription: The controlled placeholder date.\n\n**`defaultPlaceholderValue`**\nType: `DateValue`\nDescription: The initial placeholder date when rendered.\n\n**`onValueChange`**\nType: `(details: ValueChangeDetails) => void`\nDescription: Function called when the value changes.\n\n**`onPlaceholderChange`**\nType: `(details: PlaceholderChangeDetails) => void`\nDescription: A function called when the placeholder value changes.\n\n**`onFocusChange`**\nType: `(details: FocusChangeDetails) => void`\nDescription: A function called when the date input gains or loses focus.\n\n**`selectionMode`**\nType: `SelectionMode`\nDescription: The selection mode of the date input.\n- `single` - only one date can be entered\n- `range` - a range of dates can be entered (start and end)\n\n**`hourCycle`**\nType: `HourCycle`\nDescription: Whether to use 12-hour or 24-hour time format.\nBy default, this is determined by the locale.\n\n**`granularity`**\nType: `DateGranularity`\nDescription: Determines the smallest unit that is displayed in the date input.\n\n**`shouldForceLeadingZeros`**\nType: `boolean`\nDescription: Whether to always show leading zeros in month, day, and hour fields.\nWhen false, formatting follows the locale default (e.g. \"1\" instead of \"01\").\n\n**`formatter`**\nType: `DateFormatter`\nDescription: The date formatter to use.\n\n**`allSegments`**\nType: `Partial<{ year: boolean; month: boolean; day: boolean; hour: boolean; minute: boolean; second: boolean; dayPeriod: boolean; era: boolean; literal: boolean; timeZoneName: boolean; weekday: boolean; unknown: boolean; fractionalSecond: boolean; }>`\nDescription: The computed segments map for the formatter.\n\n**`format`**\nType: `(date: FormatDateDetails) => string`\nDescription: The format function for converting a DateValue to a string.\n\n**`dir`**\nType: `\"ltr\" | \"rtl\"`\nDescription: The document's text/writing direction.\n\n**`id`**\nType: `string`\nDescription: The unique identifier of the machine.\n\n**`getRootNode`**\nType: `() => ShadowRoot | Node | Document`\nDescription: A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron.\n\n### Machine API\n\nThe date input `api` exposes the following methods:\n\n**`focused`**\nType: `boolean`\nDescription: Whether the date input is focused\n\n**`disabled`**\nType: `boolean`\nDescription: Whether the date input is disabled\n\n**`invalid`**\nType: `boolean`\nDescription: Whether the date input is invalid\n\n**`value`**\nType: `DateValue[]`\nDescription: The selected date(s).\n\n**`valueAsDate`**\nType: `Date[]`\nDescription: The selected date(s) as Date objects.\n\n**`valueAsString`**\nType: `string[]`\nDescription: The selected date(s) as strings.\n\n**`placeholderValue`**\nType: `DateValue`\nDescription: The placeholder date.\n\n**`displayValues`**\nType: `IncompleteDate[]`\nDescription: Per-group editing state. Each IncompleteDate tracks which segments have been\nfilled in by the user (non-null = entered, null = placeholder).\n\n**`focus`**\nType: `VoidFunction`\nDescription: Focuses the first segment.\n\n**`setValue`**\nType: `(values: DateValue[]) => void`\nDescription: Sets the selected date(s) to the given values.\n\n**`clearValue`**\nType: `VoidFunction`\nDescription: Clears the selected date(s).\n\n**`getSegments`**\nType: `(props?: SegmentsProps) => DateSegment[]`\nDescription: Returns the segments for the given index.\n\n**`getSegmentState`**\nType: `(props: SegmentProps) => SegmentState`\nDescription: Returns the state details for a given segment.\n\n### Data Attributes\n\n**`Root`**\n\n**`data-scope`**: date-input\n**`data-part`**: root\n**`data-disabled`**: Present when disabled\n**`data-readonly`**: Present when read-only\n**`data-invalid`**: Present when invalid\n\n**`Label`**\n\n**`data-scope`**: date-input\n**`data-part`**: label\n**`data-disabled`**: Present when disabled\n**`data-readonly`**: Present when read-only\n**`data-invalid`**: Present when invalid\n\n**`Control`**\n\n**`data-scope`**: date-input\n**`data-part`**: control\n**`data-disabled`**: Present when disabled\n**`data-readonly`**: Present when read-only\n**`data-invalid`**: Present when invalid\n**`data-focus`**: Present when focused\n\n**`SegmentGroup`**\n\n**`data-scope`**: date-input\n**`data-part`**: segment-group\n**`data-disabled`**: Present when disabled\n**`data-readonly`**: Present when read-only\n**`data-invalid`**: Present when invalid\n**`data-focus`**: Present when focused\n\n**`Segment`**\n\n**`data-scope`**: date-input\n**`data-part`**: segment\n**`data-type`**: The type of the item\n**`data-readonly`**: Present when read-only\n**`data-disabled`**: Present when disabled\n**`data-value`**: The value of the item\n**`data-editable`**: \n**`data-placeholder-shown`**: Present when placeholder is shown\n\n### CSS Variables\n\n<CssVarTable name=\"date-input\" />","package":"@zag-js/date-input","editUrl":"https://github.com/chakra-ui/zag/edit/main/website/data/components/date-input.mdx"}