{"slug":"number-input","title":"Number Input","description":"Using the numeric input machine in your project.","contentType":"component","framework":"react","content":"The number input provides controls for editing, incrementing or decrementing\nnumeric values using the keyboard or pointer.\n\n## Resources\n\n\n[Latest version: v1.35.3](https://www.npmjs.com/package/@zag-js/number-input)\n[Logic Visualizer](https://zag-visualizer.vercel.app/number-input)\n[Source Code](https://github.com/chakra-ui/zag/tree/main/packages/machines/number-input)\n\n\n\n**Features**\n\n- Based on the spinbutton pattern\n- Supports using the scroll wheel to increment and decrement the value\n- Handles floating point rounding errors when incrementing, decrementing, and\n  snapping to step\n- Supports pressing and holding the spin buttons to continuously increment or\n  decrement\n- Supports rounding value to specific number of fraction digits\n- Supports scrubbing interaction\n\n## Installation\n\nInstall the number input package:\n\n```bash\nnpm install @zag-js/number-input @zag-js/react\n# or\nyarn add @zag-js/number-input @zag-js/react\n```\n\n## Anatomy\n\nTo set up the number input correctly, you'll need to understand its anatomy and\nhow we name its parts.\n\n> Each part includes a `data-part` attribute to help identify them in the DOM.\n\n\n\n## Usage\n\nImport the number input package:\n\n```jsx\nimport * as numberInput from \"@zag-js/number-input\"\n```\n\nThe number input package exports two key functions:\n\n- `machine` - State machine logic.\n- `connect` - Maps machine state to JSX props and event handlers.\n\n> Pass a unique `id` to `useMachine` so generated element ids stay predictable.\n\nThen use the framework integration helpers:\n\n```jsx\nimport * as numberInput from \"@zag-js/number-input\"\nimport { useMachine, normalizeProps } from \"@zag-js/react\"\nimport { useId } from \"react\"\n\nexport function NumberInput() {\n  const service = useMachine(numberInput.machine, { id: useId() })\n\n  const api = numberInput.connect(service, normalizeProps)\n\n  return (\n    <div {...api.getRootProps()}>\n      <label {...api.getLabelProps()}>Enter number:</label>\n      <div>\n        <button {...api.getDecrementTriggerProps()}>DEC</button>\n        <input {...api.getInputProps()} />\n        <button {...api.getIncrementTriggerProps()}>INC</button>\n      </div>\n    </div>\n  )\n}\n```\n\n### Setting the initial value\n\nSet `defaultValue` to define the initial value. The value must be a `string`.\n\n```jsx {2}\nconst service = useMachine(numberInput.machine, {\n  defaultValue: \"13\",\n})\n```\n\n### Controlled value\n\nUse `value` and `onValueChange` to control the value externally.\n\n> **Note:** Since the value can be formatted, it's important to preserve the\n> value as a string.\n\n```tsx\nimport { useState } from \"react\"\n\nexport function ControlledNumberInput() {\n  const [value, setValue] = useState(\"\")\n\n  const service = useMachine(numberInput.machine, {\n    value,\n    onValueChange(details) {\n      setValue(details.value)\n    },\n  })\n}\n```\n\n### Setting a minimum and maximum value\n\nPass the `min` prop or `max` prop to set an upper and lower limit for the input.\nBy default, the input will restrict the value to stay within the specified\nrange.\n\n```jsx {2,3}\nconst service = useMachine(numberInput.machine, {\n  min: 10,\n  max: 200,\n})\n```\n\n> To allow the value overflow the specified min or max, set the\n> `allowOverflow: true` in the context.\n\n### Validating overflow and underflow\n\nUse `onValueInvalid` to react when the value goes below `min` or above `max`.\n\n```jsx\nconst service = useMachine(numberInput.machine, {\n  min: 0,\n  max: 10,\n  allowOverflow: true,\n  onValueInvalid(details) {\n    // details => { value: string, valueAsNumber: number, reason: \"rangeUnderflow\" | \"rangeOverflow\" }\n    console.log(details.reason)\n  },\n})\n```\n\n### Scrubbing the input value\n\nNumber input supports the scrubber interaction pattern. To use this pattern,\nspread `api.getScrubberProps()` on the scrubbing element.\n\nIt uses the Pointer lock API and tracks the pointer movement. It also renders a\nvirtual cursor which mimics the real cursor's pointer.\n\n```jsx {13}\nimport * as numberInput from \"@zag-js/number-input\"\nimport { useMachine, normalizeProps } from \"@zag-js/react\"\n\nexport function NumberInput() {\n  const service = useMachine(numberInput.machine, { id: \"1\" })\n\n  const api = numberInput.connect(service, normalizeProps)\n\n  return (\n    <div {...api.getRootProps()}>\n      <label {...api.getLabelProps()}>Enter number:</label>\n      <div>\n        <div {...api.getScrubberProps()} />\n        <input {...api.getInputProps()} />\n      </div>\n    </div>\n  )\n}\n```\n\n### Using the mousewheel to change value\n\nThe number input machine exposes a way to increment/decrement the value using\nthe mouse wheel event. To activate this, pass the `allowMouseWheel` property to\nthe machine's context.\n\n```jsx {2}\nconst service = useMachine(numberInput.machine, {\n  allowMouseWheel: true,\n})\n```\n\n### Clamp value when user blurs the input\n\nIn most cases, users can type custom values in the input field. If the typed\nvalue is greater than the max, the value is reset to max when the user blurs the\ninput.\n\nTo disable this behavior, pass `clampValueOnBlur` and set to `false`.\n\n```jsx {2}\nconst service = useMachine(numberInput.machine, {\n  clampValueOnBlur: false,\n})\n```\n\n### Listening for value changes\n\nWhen the value changes, the `onValueChange` callback is invoked.\n\n```jsx {2-7}\nconst service = useMachine(numberInput.machine, {\n  onValueChange(details) {\n    // details => { value: string, valueAsNumber: number }\n    console.log(\"value is:\", details.value)\n  },\n})\n```\n\n### Listening for value commit\n\nUse `onValueCommit` to react when the value is committed (blur or Enter).\n\n```jsx\nconst service = useMachine(numberInput.machine, {\n  onValueCommit(details) {\n    // details => { value: string, valueAsNumber: number }\n    console.log(\"committed:\", details.value)\n  },\n})\n```\n\n### Listening for focus changes\n\nUse `onFocusChange` to react to focus and blur transitions.\n\n```jsx\nconst service = useMachine(numberInput.machine, {\n  onFocusChange(details) {\n    // details => { focused: boolean, value: string, valueAsNumber: number }\n    console.log(\"focused:\", details.focused)\n  },\n})\n```\n\n### Usage within forms\n\nTo use the number input within forms, set the `name` property in the machine's\ncontext.\n\n```jsx {2}\nconst service = useMachine(numberInput.machine, {\n  name: \"quantity\",\n})\n```\n\n### Adjusting the precision of the value\n\nTo format the input value to be rounded to specific decimal points, set the\n`formatOptions` and provide `Intl.NumberFormatOptions` such as\n`maximumFractionDigits` or `minimumFractionDigits`\n\n```jsx {2-5}\nconst service = useMachine(numberInput.machine, {\n  formatOptions: {\n    maximumFractionDigits: 4,\n    minimumFractionDigits: 2,\n  },\n})\n```\n\n### Disabling long press spin\n\nTo disable the long press spin, set the `spinOnPress` to `false`.\n\n```jsx {2}\nconst service = useMachine(numberInput.machine, {\n  spinOnPress: false,\n})\n```\n\n### Choosing mobile keyboard type\n\nSet `inputMode` to hint the keyboard type on mobile.\n\n```jsx\nconst service = useMachine(numberInput.machine, {\n  inputMode: \"numeric\", // \"text\" | \"tel\" | \"numeric\" | \"decimal\"\n})\n```\n\n### Format and parse value\n\nTo apply custom formatting to the input's value, set the `formatOptions` and\nprovide `Intl.NumberFormatOptions` such as `style` and `currency`\n\n```jsx {2-5}\nconst service = useMachine(numberInput.machine, {\n  formatOptions: {\n    style: \"currency\",\n    currency: \"USD\",\n  },\n})\n```\n\n### Customizing accessibility labels\n\nUse `translations` to customize increment/decrement labels and value text.\n\n```jsx\nconst service = useMachine(numberInput.machine, {\n  translations: {\n    incrementLabel: \"Increase quantity\",\n    decrementLabel: \"Decrease quantity\",\n    valueText: (value) => `${value} units`,\n  },\n})\n```\n\n### Submitting with an external form\n\nSet `form` if the hidden input should submit with a form outside the current DOM\nsubtree.\n\n```jsx\nconst service = useMachine(numberInput.machine, {\n  name: \"value\",\n  form: \"checkout-form\",\n})\n```\n\n## Styling guide\n\nEach part includes a `data-part` attribute you can target in CSS.\n\n### Disabled state\n\nWhen the number input is disabled, the root, label and input parts will have\n`data-disabled` attribute added to them.\n\nThe increment and decrement spin buttons are disabled when the number input is\ndisabled and the min/max is reached.\n\n```css\n[data-part=\"root\"][data-disabled] {\n  /* disabled styles for the input */\n}\n\n[data-part=\"input\"][data-disabled] {\n  /* disabled styles for the input */\n}\n\n[data-part=\"label\"][data-disabled] {\n  /* disabled styles for the label */\n}\n\n[data-part=\"increment-trigger\"][data-disabled] {\n  /* disabled styles for the increment button */\n}\n\n[data-part=\"decrement-trigger\"][data-disabled] {\n  /* disabled styles for the decrement button */\n}\n```\n\n### Invalid state\n\nThe number input is invalid, either by passing `invalid: true` or when the value\nexceeds the max and `allowOverflow: true` is passed. When this happens, the\nroot, label and input parts will have `data-invalid` attribute added to them.\n\n```css\n[data-part=\"root\"][data-invalid] {\n  /* disabled styles for the input */\n}\n\n[data-part=\"input\"][data-invalid] {\n  /* invalid styles for the input */\n}\n\n[data-part=\"label\"][data-invalid] {\n  /* invalid styles for the label */\n}\n```\n\n### Readonly state\n\nWhen the number input is readonly, the input part will have `data-readonly`\nadded.\n\n```css\n[data-part=\"input\"][data-readonly] {\n  /* readonly styles for the input */\n}\n```\n\n### Increment and decrement spin buttons\n\nThe spin buttons can be styled individually with their respective `data-part`\nattribute.\n\n```css\n[data-part=\"increment-trigger\"] {\n  /* styles for the increment trigger element */\n}\n\n[data-part=\"decrement-trigger\"] {\n  /* styles for the decrement trigger element */\n}\n```\n\n## Methods and Properties\n\n### Machine Context\n\nThe number input machine exposes the following context properties:\n\n**`ids`**\nType: `Partial<{ root: string; label: string; input: string; incrementTrigger: string; decrementTrigger: string; scrubber: string; }>`\nDescription: The ids of the elements in the number input. Useful for composition.\n\n**`name`**\nType: `string`\nDescription: The name attribute of the number input. Useful for form submission.\n\n**`form`**\nType: `string`\nDescription: The associate form of the input element.\n\n**`disabled`**\nType: `boolean`\nDescription: Whether the number input is disabled.\n\n**`readOnly`**\nType: `boolean`\nDescription: Whether the number input is readonly\n\n**`invalid`**\nType: `boolean`\nDescription: Whether the number input value is invalid.\n\n**`required`**\nType: `boolean`\nDescription: Whether the number input is required\n\n**`pattern`**\nType: `string`\nDescription: The pattern used to check the <input> element's value against\n\n**`value`**\nType: `string`\nDescription: The controlled value of the input\n\n**`defaultValue`**\nType: `string`\nDescription: The initial value of the input when rendered.\nUse when you don't need to control the value of the input.\n\n**`min`**\nType: `number`\nDescription: The minimum value of the number input\n\n**`max`**\nType: `number`\nDescription: The maximum value of the number input\n\n**`step`**\nType: `number`\nDescription: The amount to increment or decrement the value by\n\n**`allowMouseWheel`**\nType: `boolean`\nDescription: Whether to allow mouse wheel to change the value\n\n**`allowOverflow`**\nType: `boolean`\nDescription: Whether to allow the value overflow the min/max range\n\n**`clampValueOnBlur`**\nType: `boolean`\nDescription: Whether to clamp the value when the input loses focus (blur)\n\n**`focusInputOnChange`**\nType: `boolean`\nDescription: Whether to focus input when the value changes\n\n**`translations`**\nType: `IntlTranslations`\nDescription: Specifies the localized strings that identifies the accessibility elements and their states\n\n**`formatOptions`**\nType: `Intl.NumberFormatOptions`\nDescription: The options to pass to the `Intl.NumberFormat` constructor\n\n**`inputMode`**\nType: `InputMode`\nDescription: Hints at the type of data that might be entered by the user. It also determines\nthe type of keyboard shown to the user on mobile devices\n\n**`onValueChange`**\nType: `(details: ValueChangeDetails) => void`\nDescription: Function invoked when the value changes\n\n**`onValueInvalid`**\nType: `(details: ValueInvalidDetails) => void`\nDescription: Function invoked when the value overflows or underflows the min/max range\n\n**`onFocusChange`**\nType: `(details: FocusChangeDetails) => void`\nDescription: Function invoked when the number input is focused\n\n**`onValueCommit`**\nType: `(details: ValueChangeDetails) => void`\nDescription: Function invoked when the value is committed (when the input is blurred or the Enter key is pressed)\n\n**`spinOnPress`**\nType: `boolean`\nDescription: Whether to spin the value when the increment/decrement button is pressed\n\n**`locale`**\nType: `string`\nDescription: The current locale. Based on the BCP 47 definition.\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 number input `api` exposes the following methods:\n\n**`focused`**\nType: `boolean`\nDescription: Whether the input is focused.\n\n**`invalid`**\nType: `boolean`\nDescription: Whether the input is invalid.\n\n**`empty`**\nType: `boolean`\nDescription: Whether the input value is empty.\n\n**`value`**\nType: `string`\nDescription: The formatted value of the input.\n\n**`valueAsNumber`**\nType: `number`\nDescription: The value of the input as a number.\n\n**`setValue`**\nType: `(value: number) => void`\nDescription: Function to set the value of the input.\n\n**`clearValue`**\nType: `VoidFunction`\nDescription: Function to clear the value of the input.\n\n**`increment`**\nType: `VoidFunction`\nDescription: Function to increment the value of the input by the step.\n\n**`decrement`**\nType: `VoidFunction`\nDescription: Function to decrement the value of the input by the step.\n\n**`setToMax`**\nType: `VoidFunction`\nDescription: Function to set the value of the input to the max.\n\n**`setToMin`**\nType: `VoidFunction`\nDescription: Function to set the value of the input to the min.\n\n**`focus`**\nType: `VoidFunction`\nDescription: Function to focus the input.\n\n### Data Attributes\n\n**`Root`**\n\n**`data-scope`**: number-input\n**`data-part`**: root\n**`data-disabled`**: Present when disabled\n**`data-focus`**: Present when focused\n**`data-invalid`**: Present when invalid\n**`data-scrubbing`**: \n\n**`Label`**\n\n**`data-scope`**: number-input\n**`data-part`**: label\n**`data-disabled`**: Present when disabled\n**`data-focus`**: Present when focused\n**`data-invalid`**: Present when invalid\n**`data-required`**: Present when required\n**`data-scrubbing`**: \n\n**`Control`**\n\n**`data-scope`**: number-input\n**`data-part`**: control\n**`data-focus`**: Present when focused\n**`data-disabled`**: Present when disabled\n**`data-invalid`**: Present when invalid\n**`data-scrubbing`**: \n\n**`ValueText`**\n\n**`data-scope`**: number-input\n**`data-part`**: value-text\n**`data-disabled`**: Present when disabled\n**`data-invalid`**: Present when invalid\n**`data-focus`**: Present when focused\n**`data-scrubbing`**: \n\n**`Input`**\n\n**`data-scope`**: number-input\n**`data-part`**: input\n**`data-invalid`**: Present when invalid\n**`data-disabled`**: Present when disabled\n**`data-scrubbing`**: \n\n**`DecrementTrigger`**\n\n**`data-scope`**: number-input\n**`data-part`**: decrement-trigger\n**`data-disabled`**: Present when disabled\n**`data-scrubbing`**: \n\n**`IncrementTrigger`**\n\n**`data-scope`**: number-input\n**`data-part`**: increment-trigger\n**`data-disabled`**: Present when disabled\n**`data-scrubbing`**: \n\n**`Scrubber`**\n\n**`data-scope`**: number-input\n**`data-part`**: scrubber\n**`data-disabled`**: Present when disabled\n**`data-scrubbing`**: \n\n## Accessibility\n\n### Keyboard Interactions\n\n**`ArrowUp`**\nDescription: Increments the value of the number input by a predefined step.\n\n**`ArrowDown`**\nDescription: Decrements the value of the number input by a predefined step.\n\n**`PageUp`**\nDescription: Increments the value of the number input by a larger predefined step.\n\n**`PageDown`**\nDescription: Decrements the value of the number input by a larger predefined step.\n\n**`Home`**\nDescription: Sets the value of the number input to its minimum allowed value.\n\n**`End`**\nDescription: Sets the value of the number input to its maximum allowed value.\n\n**`Enter`**\nDescription: Submits the value entered in the number input.","package":"@zag-js/number-input","editUrl":"https://github.com/chakra-ui/zag/edit/main/website/data/components/number-input.mdx"}