# Number Input

> Number entry three ways: NumberInput with − / + steppers, Slider for a value on a known scale, OtpInput for one-time codes.

Component · Forms · Pro kit · [Live page](https://ui.twentyfirst.media/pro/components/NumberInput/)

Number entry three ways: `NumberInput` with − / + steppers, `Slider` for a value on a known scale, `OtpInput` for one-time codes.

NumberInput: `label`, `value`/`defaultValue`, `onChange(n)`, `min`, `max`, `step`, `unit`, `hint`.
Slider: `label`, `min`, `max`, `step`, `value`/`defaultValue`, `onChange(n)`, `format(v)`, `marks` (false hides min/max), `hint`.
OtpInput: `length` (6), `onComplete(code)`, `error` (shakes and turns red), `label`.

- NumberInput for small exact counts (seats, days); arrow keys step too.
- Slider when the range matters more than the exact value (budgets, thresholds); always show the value bubble.
- OtpInput moves focus forward as you type, back on Backspace, and fills every box on paste.

## Example

```tsx
import * as React from "react";
import { NumberInput, OtpInput, Slider } from "@twentyfirstmedia/pro";

function App() {
  const s = React.useState(12),
    b = React.useState(120);
  return (
    <div
      className="tf-panel"
      style={{
        gap: 24,
      }}
    >
      <div
        style={{
          display: "flex",
          gap: 32,
          flexWrap: "wrap",
          alignItems: "flex-start",
        }}
      >
        <NumberInput
          label="Seats"
          value={s[0]}
          onChange={s[1]}
          min={1}
          max={200}
          hint="€24 per seat"
        />
        <NumberInput label="VAT" defaultValue={19} min={0} max={25} unit="%" />
        <div className="tf-field">
          <span className="tf-field-label">Verification code</span>
          <OtpInput onComplete={() => {}} />
          <span className="tf-field-hint">Paste works too</span>
        </div>
      </div>
      <Slider
        label="Monthly agent budget"
        min={0}
        max={500}
        step={10}
        value={b[0]}
        onChange={b[1]}
        format={(v) => "€" + v}
        hint="Agents pause and ask you once they reach it"
      />
    </div>
  );
}
export default App;
```

## Props

### NumberInput

| Prop | Type | Notes |
| --- | --- | --- |
| `label?` | `string` |  |
| `value?` | `number` |  |
| `defaultValue?` | `number` |  |
| `onChange?` | `(n: number) => void` |  |
| `min?` | `number` |  |
| `max?` | `number` |  |
| `step?` | `number` |  |
| `unit?` | `string` |  |
| `hint?` | `string` |  |
| `error?` | `string` |  |
| `className?` | `string` | Extra class on the root element. |
| `style?` | `CSSProperties` | Inline style on the root element. |

### OtpInput

| Prop | Type | Notes |
| --- | --- | --- |
| `length?` | `number` |  |
| `onComplete?` | `(code: string) => void` |  |
| `error?` | `boolean` |  |
| `clearOnError?` | `boolean` | Empties the cells when `error` turns on, ready for the next try (default true). |
| `ref?` | `Ref<OtpInputHandle>` | `ref.current.reset()` empties the cells on demand. |
| `label?` | `string` |  |
| `className?` | `string` | Extra class on the root element. |
| `style?` | `CSSProperties` | Inline style on the root element. |

### Slider

| Prop | Type | Notes |
| --- | --- | --- |
| `label?` | `string` |  |
| `min?` | `number` |  |
| `max?` | `number` |  |
| `step?` | `number` |  |
| `value?` | `number` |  |
| `defaultValue?` | `number` |  |
| `onChange?` | `(n: number) => void` |  |
| `format?` | `(n: number) => string` |  |
| `marks?` | `boolean` |  |
| `hint?` | `string` |  |
| `className?` | `string` | Extra class on the root element. |
| `style?` | `CSSProperties` | Inline style on the root element. |
