> ## Documentation Index
> Fetch the complete documentation index at: https://docs.twenty.com/llms.txt
> Use this file to discover all available pages before exploring further.

# NumberStepper

> Edit a number with optional bounds and increment or decrement buttons.

export const StoryEmbed = ({storyId, title, height = 240}) => <>
    <Tabs>
      <Tab title="Light">
        <iframe title={`${title} (light)`} src={`https://storybook.twenty.com/iframe.html?id=${storyId}&viewMode=story&globals=colorScheme:light`} width="100%" height={height} loading="lazy" style={{
  border: 0
}} />
      </Tab>
      <Tab title="Dark">
        <iframe title={`${title} (dark)`} src={`https://storybook.twenty.com/iframe.html?id=${storyId}&viewMode=story&globals=colorScheme:dark`} width="100%" height={height} loading="lazy" style={{
  border: 0
}} />
      </Tab>
    </Tabs>
    <a href={`https://storybook.twenty.com/?path=/story/${storyId}`}>
      Open in Storybook
    </a>
  </>;

`NumberStepper` combines a compact numeric text field with decrement and increment buttons. It owns numeric editing, bounds, and step interactions. Your application chooses the limits and handles persistence.

<StoryEmbed storyId="ui-input-numberstepper--default" title="NumberStepper example" height={180} />

## Uncontrolled value

Use `defaultValue` to set the initial quantity. The input owns subsequent changes.

```tsx theme={null}
import { Field, NumberStepper } from 'twenty-ui/primitives/input';

export const QuantityInput = () => (
  <Field.Root>
    <Field.Label>Quantity</Field.Label>
    <NumberStepper name="quantity" defaultValue={3} min={0} max={10} step={1} />
    <Field.Description>Choose up to ten items.</Field.Description>
  </Field.Root>
);
```

## Controlled value

Pass a number or `null` through `value` and update it in `onValueChange` to own the same quantity in React state. The callback receives the next numeric value and event details.

```tsx theme={null}
import { useState } from 'react';
import { Field, NumberStepper } from 'twenty-ui/primitives/input';

export const QuantityInput = () => {
  const [quantity, setQuantity] = useState<number | null>(3);

  return (
    <Field.Root>
      <Field.Label>Quantity</Field.Label>
      <NumberStepper
        name="quantity"
        value={quantity}
        onValueChange={setQuantity}
        min={0}
        max={10}
        step={1}
      />
      <Field.Description>Choose up to ten items.</Field.Description>
    </Field.Root>
  );
};
```

Clearing the field reports `null`. Incomplete text such as `-` or `+` remains editable without reporting an invalid number until it is replaced or stepped. By default, parseable numeric edits report the bounded value; the displayed draft is normalized on blur. Values equal to the current value do not call `onValueChange` again.

## Application validation

Set `allowOutOfRange` when the application needs the parsed value before applying its own limits or validation. `onValueChange` then receives typed and pasted numbers outside the range. Uncontrolled fields, or controlled fields that accept those values, preserve them on blur. Controlled fields that reject them return to their accepted value on blur. For example, replacing `60` with `45` reports `4` and then `45`, even when `min={30}`. The application can ignore the intermediate value without accidentally saving the minimum.

Buttons and keyboard adjustments still stop at `min` and `max`. Native form validation prevents submission while an accepted value is outside that range. `allowOutOfRange` defaults to `false`.

## Bounds and interaction

`min` and `max` are inclusive. Increment and decrement buttons stop at the matching bound. Arrow Up and Arrow Down adjust the focused input by `step`, which defaults to `1`. Home and End select the corresponding bound when provided; call `event.preventBaseUIHandler()` in `onKeyDown` to keep a key's text editing behavior instead. The same step applies when Shift or Alt is held. Displayed values support up to 15 fraction digits; use a positive step within that precision for decimal adjustments.

Values use Latin digits and show grouping separators from five integer digits, such as `12,500`. Typed and pasted group separators of the browser's locale are accepted.

`step` controls adjustments; it does not round typed values to a step. When `min` is set, native form validation also requires `min` plus a multiple of `step`: with the default `step` of `1`, a typed `2.5` blocks submission. Set `showButtons={false}` for text editing with the same keyboard behavior.

Each button has a default accessible name. Use `decrementLabel` and `incrementLabel` to provide contextual or translated names. Give the input a visible `Field.Label`, `aria-label`, or `aria-labelledby`.

## Forms and focus

Use `name` to include the numeric value in a form. `form` associates it with a form elsewhere in the document. The adjustment buttons use `type="button"`, so adjusting a value does not submit the form. `required` participates in native validation.

Use a controlled `value` when your application needs to reset the field. A native form reset does not reset the numeric state.

`disabled` prevents changes and excludes the value from form submission. `readOnly` prevents changes while keeping the input focusable and its value available to the form.

Native input attributes and handlers, `className`, `style`, and `ref` apply to the visible input. For example, call `ref.current?.focus()` to focus the field. Combine the input with `Field.Description` and `Field.Error` for guidance and validation messages.

## Front component renderer

The React and Preact renderer sandboxes support keyboard adjustments, bounds, and form values. Pointer stepping and text editing are currently blocked by missing input selection and native event support in the renderer. Pasting is not supported: without input selection, the pasted text is inserted around the whole value, that wrong number is reported, and the front component then stops rendering. These limitations do not affect normal React rendering.

## Props

<ParamField body="allowOutOfRange" type="boolean" default="false">
  Allows onValueChange to receive typed and pasted numbers outside the bounds. Native range validation still applies; buttons and keyboard adjustments remain bounded.
</ParamField>

<ParamField body="aria-roledescription" type="string" default="Number field">
  A user-friendly description of the input's role for assistive tech. This is a role
  description, not an accessible name — use `Field.Label` or `aria-label` to name the control.
</ParamField>

<ParamField body="className" type="string | ((state: NumberFieldInputState) => string | undefined)">
  Class applied to the visible input.
</ParamField>

<ParamField body="decrementLabel" type="string" default="Decrease value">
  Accessible name for the decrement button.
</ParamField>

<ParamField body="defaultValue" type="number">
  Initial number when the field owns its value.
</ParamField>

<ParamField body="disabled" type="boolean" default="false">
  Disables the input and both buttons, and omits the form value.
</ParamField>

<ParamField body="form" type="string">
  ID of the form that owns the submitted numeric value.
</ParamField>

<ParamField body="id" type="string">
  ID of the visible input. Generated when omitted.
</ParamField>

<ParamField body="incrementLabel" type="string" default="Increase value">
  Accessible name for the increment button.
</ParamField>

<ParamField body="max" type="number">
  Inclusive maximum. By default, onValueChange reports bounded numbers and out-of-range drafts normalize on blur.
</ParamField>

<ParamField body="min" type="number">
  Inclusive minimum. By default, onValueChange reports bounded numbers and out-of-range drafts normalize on blur.
</ParamField>

<ParamField body="name" type="string">
  Name of the numeric value submitted with a form.
</ParamField>

<ParamField body="onValueChange" type="((value: number | null, eventDetails: NumberFieldRootChangeEventDetails) => void)">
  Called with the next number or null and event details when the numeric value changes. Clearing reports null; incomplete text does not report a number.
</ParamField>

<ParamField body="readOnly" type="boolean" default="false">
  Prevents numeric changes while keeping the input focusable.
</ParamField>

<ParamField body="ref" type="Ref<HTMLInputElement>">
  Ref to the visible input element.
</ParamField>

<ParamField body="render" type="ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<DetailedHTMLProps<InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>, NumberFieldInputState>">
  Allows you to replace the component's HTML element
  with a different tag, or compose it with another component.

  Accepts a `ReactElement` or a function that returns the element to render.
</ParamField>

<ParamField body="required" type="boolean" default="false">
  Requires a numeric value for native form submission.
</ParamField>

<ParamField body="showButtons" type="boolean" default="true">
  Shows the decrement and increment buttons.
</ParamField>

<ParamField body="step" type="number" default="1">
  Amount added or subtracted by the buttons and arrow keys.
</ParamField>

<ParamField body="style" type="CSSProperties | ((state: NumberFieldInputState) => CSSProperties | undefined)">
  Inline styles applied to the visible input.
</ParamField>

<ParamField body="value" type="number | null">
  Controlled numeric value. Use null for an empty field.
</ParamField>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.