Components / Text & numbers

NumberInput

A numeric field with steppers, min/max clamping and arrow-key stepping.

@ruckc/truss-ui Text & numbers

Import

import { NumberInput } from '@ruckc/truss-ui';

Playground

min
max
step
value

Recipe: reacting to committed changes

onchange receives the new value (number | null) each time a change is committed: on blur, on Enter, with the arrow keys and with the step buttons. It does not fire on every keystroke, and not when the committed value equals the current one.

Last change: none

<NumberInput label="Quantity" bind:value={qty} onchange={(v) => save(v)} />

Recipe: function bindings for immutable state

When state is updated immutably, bind with a getter and a setter instead of bind:value={x}. The setter is the place to copy, validate or clamp.

<script lang="ts">
  let state = $state({ qty: 3 });
  const update = (v: number | null) => (state = { ...state, qty: v ?? 0 });
</script>

<!-- Svelte function bindings: a getter and a setter -->
<NumberInput label="Quantity" bind:value={() => state.qty, (v) => update(v)} />

Recipe: clearing the field

Clearing the input commits null, so a consumer that needs a number must handle it (value ?? 0). Set allowEmpty={false} to commit min ?? 0 instead and never see null from user input.

Value: 2

<NumberInput label="Seats" min={1} allowEmpty={false} bind:value={seats} />
<!-- clearing the field commits 1 (min ?? 0), never null -->

Recipe: an optional number

Some settings are a number or "off". With nullable, NumberInput and Slider add a switch named "Enable label" to the same labelled control. While the value is null the field is inert and shows offLabel (default "Off"). Switching on restores the last number the control had, else defaultValue, else min (or 0). A range slider ignores nullable. An enabled NumberInput that is cleared keeps its number: turn the switch off to get null.

Value:

Speed
40

Value: 40

<NumberInput label="Max iterations" nullable defaultValue={100} bind:value={limit} />
<Slider label="Speed" nullable numberInput offLabel="Auto" bind:value={speed} />
<!-- null means off; the built-in switch is named "Enable Max iterations" -->

API

PropTypeDefaultDescription
value bindable number | nullnullThe current value. Bind it with `bind:value`.
min number—Minimum allowed value.
max number—Maximum number to show before collapsing the rest.
step number1Increment between values.
label string—Visible label shown above the control.
ariaLabel string—Accessible name when no visible `label` is shown.
hint string—Helper text shown below the control.
error string—Error message. Marks the control invalid, replaces the hint, and is announced to assistive technology.
placeholder string—Hint shown while the field is empty.
disabled booleanfalseDisables the control: it cannot be focused, changed or activated.
allowEmpty booleantrueWhen false, clearing the field commits `min ?? 0` instead of `null`. Defaults to true.
nullable booleanfalseTreat `null` as "off": a built-in switch (named "Enable <label>") sits beside the field, the field is disabled and shows `offLabel` while off. Clearing the text of an enabled field keeps the current number instead of committing `null`; turn the switch off for that.
offLabel string'Off'Text shown while a `nullable` field is off. Defaults to "Off".
defaultValue number—What turning a `nullable` field on restores when there is no earlier number to return to. Falls back to `min ?? 0`.
onchange event(value: number | null) => void—Fires when a change is committed (blur, Enter, arrow keys, the step buttons), not on every keystroke.
/docs/number-input Truss UI · MIT