Skip to main content

Numberinput

Overview​

The Numberinput component provides a number input with increment/decrement buttons.

It's ideal for quantity selectors, step inputs, and any numeric value that benefits from easy +/- adjustment.


Import​

import { Numberinput } from '@allxsmith/bestax-bulma';

Usage​

Basic Numberinput​

A simple number input with +/- buttons.

function example() {
  const [value, setValue] = useState(5);
  return (
    <Block>
      <Numberinput value={value} onChange={setValue} />
      <Paragraph mt="2">Value: {value}</Paragraph>
    </Block>
  );
}


With Min and Max​

Number input with value constraints.

function example() {
  const [quantity, setQuantity] = useState(1);
  return (
    <Block>
      <Paragraph mb="2">Quantity (1-10):</Paragraph>
      <Numberinput
        value={quantity}
        onChange={setQuantity}
        min={1}
        max={10}
        color="primary"
      />
    </Block>
  );
}


Color Variants​

Number inputs with different button colors.

<Block display="flex" flexDirection="column" gap="4">
  <Numberinput defaultValue={5} color="primary" />
  <Numberinput defaultValue={5} color="success" />
  <Numberinput defaultValue={5} color="info" />
  <Numberinput defaultValue={5} color="warning" />
  <Numberinput defaultValue={5} color="danger" />
</Block>


Size Variants​

Number inputs in different sizes.

<Block display="flex" flexDirection="column" gap="4">
  <Numberinput defaultValue={5} size="small" color="primary" />
  <Numberinput defaultValue={5} color="primary" />
  <Numberinput defaultValue={5} size="medium" color="primary" />
  <Numberinput defaultValue={5} size="large" color="primary" />
</Block>


Controls Position​

Different positions for the +/- buttons.

<Block display="flex" flexDirection="column" gap="4">
  <Block>
    <Paragraph mb="1">Both sides (default)</Paragraph>
    <Numberinput defaultValue={5} controlsPosition="both" color="primary" />
  </Block>
  <Block>
    <Paragraph mb="1">Left only</Paragraph>
    <Numberinput defaultValue={5} controlsPosition="left" color="info" />
  </Block>
  <Block>
    <Paragraph mb="1">Right only</Paragraph>
    <Numberinput defaultValue={5} controlsPosition="right" color="success" />
  </Block>
</Block>


Rounded Buttons​

Number input with rounded +/- buttons.

<Numberinput defaultValue={5} controlsRounded color="primary" />


Custom Step​

Number input with custom step increment.

function example() {
  const [value, setValue] = useState(0);
  return (
    <Block>
      <Paragraph mb="2">Step by 10:</Paragraph>
      <Numberinput
        value={value}
        onChange={setValue}
        step={10}
        min={0}
        max={100}
        color="info"
      />
      <Paragraph mt="2">Value: {value}</Paragraph>
    </Block>
  );
}


Read-only Input​

Number input where you can only use the buttons (not type directly).

<Numberinput defaultValue={5} editable={false} color="primary" />


Disabled State​

A disabled number input.

<Numberinput defaultValue={5} disabled />


Context-Aware Rendering​

The Numberinput component is context-aware: it detects whether it is already inside a Field and adjusts its rendering accordingly. This means you can use it standalone with a label prop (it wraps itself in a Field), or inside a Field (it skips rendering its own).

note

Numberinput does not use ControlContext, so the "With Field and Control Wrappers" example below uses Field wrapping only. The Control wrapper is shown for layout consistency but does not change the component's internal rendering.

Default (with label)​

The simplest usage — the component automatically renders its own Field wrapper.

<Numberinput
  label="Quantity"
  defaultValue={1}
  min={1}
  max={10}
  color="primary"
/>


With Field Wrapper​

When you need manual control over the Field layout (e.g., horizontal forms), wrap the component in Field. The component detects it's inside a Field and skips rendering its own.

function example() {
  return (
    <Field horizontal label="Quantity">
      <Field.Body>
        <Field>
          <Numberinput defaultValue={1} min={1} max={10} color="primary" />
        </Field>
      </Field.Body>
    </Field>
  );
}


With Field and Control Wrappers​

For full manual composition, wrap in both Field and Control. Numberinput does not consume ControlContext, but the Field wrapper is still detected and its own Field is skipped.

function example() {
  return (
    <Field horizontal label="Quantity">
      <Field.Body>
        <Field>
          <Control iconLeftName="hashtag">
            <Numberinput defaultValue={1} min={1} max={10} color="primary" />
          </Control>
        </Field>
      </Field.Body>
    </Field>
  );
}


Controlled vs Uncontrolled​

Controlled Mode​

Use value and onChange to manage state externally:

const [quantity, setQuantity] = useState(1);
<Numberinput value={quantity} onChange={setQuantity} min={1} max={10} />;

Uncontrolled Mode​

Use defaultValue for internal state management:

<Numberinput defaultValue={5} min={0} max={100} />

Accessibility​

  • Uses native number input element
  • Has aria-valuenow, aria-valuemin, and aria-valuemax attributes
  • +/- buttons have aria-label for screen readers
  • Arrow keys increment/decrement the value
  • Buttons are disabled at min/max boundaries

  • Slider - For selecting values with a range slider
  • Input - For general text/number input

Additional Resources​

Pro Tip

Set editable={false} when you want users to only use the +/- buttons, preventing manual typing errors.


Props​

PropTypeDefaultDescription
valuenumber—Controlled numeric value.
defaultValuenumber—Default value for uncontrolled usage. Initial value for uncontrolled mode.
minnumber—Minimum allowed value.
maxnumber—Maximum allowed value.
stepnumber1Step increment (default: 1).
size'small' | 'medium' | 'large'—Size variant. Size of the input and buttons.
color'primary' | 'link' | 'info' | 'success' | 'warning' | 'danger' | 'light' | 'dark'—Color variant for buttons. Color for the control buttons.
inputColor'primary' | 'link' | 'info' | 'success' | 'warning' | 'danger'—Color of the input field itself. Color for the inner input element.
controlsPosition'left' | 'right' | 'both''both'Position of increment/decrement buttons (default: 'both').
controlsRoundedbooleanfalseWhether the control buttons are rounded.
compactbooleanfalseUses compact button spacing. Whether to use compact (addons) layout.
barebooleanfalseRemoves button borders and background. Bare mode: no outer .field wrapper, for composing inside a parent Field.
variant'plusminus' | 'stepper''plusminus'Style variant for the control buttons. Control variant: 'plusminus' (default) or 'stepper'.
disabledbooleanfalseWhether the input is disabled.
editablebooleantrueWhether the input can be typed in. Whether the user can type directly into the input (default: true).
isLoadingbooleanfalseShows a loading state. Whether to show a loading spinner on the input.
exponentialbooleanfalseEnables exponential step increments when holding buttons. Whether the step grows with the value magnitude.
onChange(value: number) => void—Callback when the value changes.
labelReact.ReactNode—Field label, rendered above the widget.
labelSize'small' | 'normal' | 'medium' | 'large'—Size for the label (used in horizontal layouts).
labelPropsReact.LabelHTMLAttributes<HTMLLabelElement> & { [key: string]: unknown; }—Props for the label element.
horizontalbooleanfalseHorizontal field layout.
messageReact.ReactNode—Help/validation message below the input.
messageColor'primary' | 'link' | 'info' | 'success' | 'warning' | 'danger'—Bulma color for the message.
fieldClassNamestring—Additional CSS classes for the Field wrapper.
childrenReact.ReactNode—Content rendered inside the component.
classNamestring—Additional CSS classes.
refReact.Ref<HTMLElement>—Ref forwarded to the input element.
...All standard <input> attributes and Bulma helper props—See Helper Props

CSS & Sass Variables​

Numberinput registers these variables on its own .numberinput element. Override them there (or via className) — a value set on an ancestor is only inherited, and loses to the component-level declaration. See Theme.

CSS VariableSass VariableDefault
--bulma-numberinput-stepper-width$numberinput-stepper-width2.5em
--bulma-numberinput-stepper-border-color$numberinput-stepper-border-colorvar(--bulma-border)
--bulma-numberinput-stepper-button-color$numberinput-stepper-button-colorvar(--bulma-text-weak)
--bulma-numberinput-stepper-button-hover-color$numberinput-stepper-button-hover-colorvar(--bulma-text-strong)
--bulma-numberinput-stepper-button-hover-bg$numberinput-stepper-button-hover-bgvar(--bulma-background)
--bulma-numberinput-disabled-opacity$numberinput-disabled-opacity0.5
--bulma-numberinput-rounded-button-size$numberinput-rounded-button-size2.5em