Browse docs
Browse docs
A numeric input. Looks like a TextField but the bridge value is always number | null — empty string never leaks out, NaN never sneaks in. Optional stepper buttons (+/−) for touch ergonomics; native browser spinner suppressed via CSS so the visual stays clean.
import { NumberField } from '@dashforge/tw';
<NumberField name="age" label="Age" min={0} max={120} />import { DashForm } from '@dashforge/forms';
import { NumberField, Button } from '@dashforge/tw';
<DashForm onSubmit={onSubmit}>
<NumberField
name="quantity"
label="Quantity"
min={1}
max={99}
step={1}
showStepper
required
/>
<Button type="submit" color="primary">Add to cart</Button>
</DashForm>Standalone:
const [n, setN] = useState<number | null>(0);
<NumberField name="qty" value={n} onChange={(e) => setN(e.target.valueAsNumber ?? null)} />Between 1 and 99.
import { NumberField, Stack } from '@dashforge/tw';
<Stack gap={3}>
<NumberField name="age" label="Age" placeholder="0" />
<NumberField
name="quantity"
label="Quantity"
defaultValue={1}
min={1}
max={99}
helperText="Between 1 and 99."
/>
</Stack>Use the +/− buttons or type a number.
import { NumberField, Stack } from '@dashforge/tw';
<Stack gap={3}>
<NumberField
name="seats"
label="Seats"
defaultValue={3}
min={1}
max={20}
showStepper
helperText="Use the +/− buttons or type a number."
/>
<NumberField
name="price"
label="Price"
defaultValue={9.99}
step={0.5}
min={0}
showStepper
/>
</Stack><NumberField name="qty" min={0} max={10} step={1} showStepper />Stepper renders +/− buttons inside the field. Tap-friendly on mobile; respects min/max automatically. Hidden by default (showStepper={false}) for desktop-first forms.
<NumberField name="price" min={0} step={0.01} placeholder="0.00" />
<NumberField name="rating" min={0} max={5} step={0.5} />step accepts decimals. The bridge value preserves the precision the user typed (no implicit rounding).
<NumberField size="sm" name="x" />
<NumberField size="md" name="x" /> {/* default */}
<NumberField size="lg" name="x" /><NumberField
name="age"
label="Age"
min={18}
max={120}
required
rules={{
min: { value: 18, message: 'Must be 18 or over' },
max: { value: 120, message: 'Must be 120 or under' },
}}
/>The min/max props enforce the HTML constraint (native validation, stepper bounds); the rules min/max produce the error message in the helper slot via RHF. Both layers complement each other — use rules when you need a custom message.
<Checkbox name="isCompany" label="Buying for a company?" />
<NumberField
name="employeeCount"
label="Number of employees"
visibleWhen={(engine) => engine.getNode('isCompany')?.value === true}
min={1}
/>Configure <NumberField> defaults application-wide.
import { patchTheme } from '@dashforge/tw-theme';
patchTheme({
components: {
NumberField: {
defaults: { size: 'md', layout: 'stacked', fullWidth: false },
},
},
});Configurable axes (NumberFieldVariantProps):
| Axis | Type | Notes |
|---|---|---|
size | 'sm' | 'md' | 'lg' | Wrapper height + padding + font-size. |
layout | 'stacked' | 'inline' | Label position (above vs left). |
fullWidth | boolean | Stretch root + wrapper to container width. |
Non-visual axes (name, rules, min, max, step, showStepper, label, helperText, error, disabled, access, visibleWhen, event handlers) are not theme-configurable.
Precedence chain (lowest → highest):
defaultVariants from the internal numberFieldVariants recipe.theme.components.NumberField.defaults (application-wide).<NumberField size="lg" />) — wins over theme.sx — appended to the root and wins over conflicting classes via tailwind-merge.TypeScript: theme.components.NumberField.defaults autocompletes to the three axes above; unrecognized fields are compile-time errors.
Reactivity: useComponentDefaults('NumberField') subscribes to the theme store — patchTheme re-renders every mounted instance inheriting the changed axis.
| Prop | Type | Default | Description |
|---|---|---|---|
name | string | — | Bridge field name (required when used inside DashFormProvider). |
access | AccessRequirement | — | RBAC access requirement (combines with explicit disabled). |
defaultValue | number | string | null | — | Default value (uncontrolled, standalone mode only). |
disabled | boolean | false | Disables the input — ORed with RBAC denied:disable. |
error | boolean | false | Explicit error semaphore. Overrides the bridge's auto-detected error. |
fullWidth | boolean | false | Stretch root wrapper + input to the container's width. |
helperText | ReactNode | — | Helper line below the input. Auto-replaced by bridge error when invalid. |
label | ReactNode | — | Visible label above (or left of, per layout) the input. |
layout | 'stacked' | 'inline' | 'stacked' | Label placement — 'stacked' (above the input) or 'inline' (left of the input). |
max | number | — | Max allowed value (passed to the input + clamps stepper). |
min | number | — | Min allowed value (passed to the input + clamps stepper). |
onBlur | FocusEventHandler<HTMLInputElement> | — | Blur handler — fires after bridge onBlur (form mode). |
onChange | ChangeEventHandler<HTMLInputElement> | — | Change handler — receives the native event; consume value via bridge. |
required | boolean | false | Renders the required * marker + sets the native required attribute. |
rules | unknown | — | RHF validation rules — opaque, forwarded to the bridge. |
showStepper | boolean | — | Show inline +/− stepper buttons inside the input wrapper. Default false. |
size | 'md' | 'sm' | 'lg' | 'md' | Density tier — drives input height + padding + font-size. |
slotProps | NumberFieldSlotProps | — | Per-slot className overrides. |
step | number | — | Stepper step size. Default 1. |
sx | string | — | Root className shortcut (cn'd with the variant root class). |
value | number | string | null | — | Controlled value (form mode reads from the bridge if omitted). |
visibleWhen | (engine: Engine) => boolean | — | Engine predicate — field not rendered when it returns false. |
...rest | Omit< InputHTMLAttributes<HTMLInputElement>, 'name' | 'type' | 'size' | 'onChange' | 'onBlur' | 'value' | 'defaultValue' > | — | Additional native attributes forwarded via Omit< InputHTMLAttributes<HTMLInputElement>, 'name' | 'type' | 'size' | 'onChange' | 'onBlur' | 'value' | 'defaultValue' >. |
<NumberField> is a compound component with 9 named slots. Each slot accepts a { className?: string } override via slotProps:
| Slot | Purpose |
|---|---|
root | Outer flex wrapper (label + inputWrapper + helper/error). |
label | The <label> element. |
requiredMark | The red * next to the label when required. |
inputWrapper | The bordered surface around the input + stepper. |
input | The numeric <input type="number">. |
stepper | The +/− button group container (only when showStepper). |
stepperButton | Each individual increment/decrement button. |
helperText | Helper text line, when not in error state. |
errorText | Helper text line, when in error state. |
Use sx for a root-level class override, slotProps when you need to reach a specific inner element (e.g. the stepper button color, or the input font weight).
number | null. Empty string from the input is normalized to null. NaN from an invalid parse never propagates — the input keeps the user's literal text but the bridge value stays null until valid.-webkit-appearance: none on the input + MozAppearance: textfield). The optional showStepper renders our own +/− buttons that respect Tailwind theming + min/max.slotProps.input prefix ($, €, …) or wrap in a custom adornment. We don't ship a <CurrencyField> (yet) — NumberField + step={0.01} covers 90% of cases.min={0} max={100} step={1} and render a % suffix via slotProps.input.