MUI Components
NPMGithub

MUINumberStepper

MUINumberStepper is MUINumberInput with always-visible - / + buttons flanking the input instead of the native browser steppers. Every numeric behaviour — number | null value, onlyIntegers, nonNegative, maxDecimalPlaces, stepAmount, min / max clamping, validation and labelling is delegated to MUINumberInput. This component only adds the buttons and disables the decrement / increment button once its min / max bound is reached.

caption renders custom content (text, an icon, or both) just below the value — e.g. a unit label like "kg" or "Degrees". captionProps styles the Box wrapping caption — its sx is merged with the component's own base caption styles (position, spacing, font size, color) rather than replacing them.

Usage

import MUINumberStepper, { MUINumberStepperProps } from '@nish1896/mui-components/mui/number-stepper';
const [quantity, setQuantity] = useState<number | null>(1);

<MUINumberStepper
  fieldName="quantity"
  value={quantity}
  onValueChange={({ newValue }) => setQuantity(newValue)}
  onlyIntegers
  min={1}
  max={10}
/>

Customizing icons

  • decrementIcon / incrementIcon — replace the default - / + icons.
  • swapButtons — swap the button positions, so the increment (+) button sits on the left of the input and the decrement (-) button on the right.
  • iconButtonProps — forwarded to both stepper IconButtons (e.g. size, sx). Interaction and accessibility props are managed by the component.
import RemoveCircleOutlineIcon from '@mui/icons-material/RemoveCircleOutline';
import AddCircleOutlineIcon from '@mui/icons-material/AddCircleOutline';

<MUINumberStepper
  fieldName="temperature"
  value={temp}
  onValueChange={({ newValue }) => setTemp(newValue)}
  decrementIcon={<RemoveCircleOutlineIcon />}
  incrementIcon={<AddCircleOutlineIcon />}
  iconButtonProps={{ size: 'medium', sx: { color: 'primary.main' } }}
/>

API

MUINumberStepperProps extends MUINumberInputProps (minus showMarkers) and also accepts most TextFieldProps, with some props excluded, including type, multiline and rows.

Props marked with * are required.

NameTypeDescription
fieldName*string
Name/path of the field. Used to derive the id, the default label, and the name attribute. This prop is required for all components.
valuenumber | null
Current numeric value of the field. undefined/null render an empty input.
onValueChange*({ newValue, event }) => void
Called on every accepted numeric change. newValue is null when the input is cleared.
minnumber
Lower bound for the value. Stepping (arrow keys / markers) clamps to this and the value is clamped on blur. nonNegative sets the lower bound to 0, but min overrides it when set.
maxnumber
Upper bound for the value. Stepping (arrow keys / markers) clamps to this and the value is clamped on blur.
nonNegativeboolean
When true, negative values cannot be entered.
onlyIntegersboolean
When true, decimal input is not allowed. Cannot be combined with maxDecimalPlaces.
maxDecimalPlacesnumber
Maximum number of decimal places accepted while typing.
stepAmountnumber
Amount the value changes on Arrow Up/Down key presses.
decrementIconReactNode
Custom icon for the decrement (-) button.
Default: Material UI Remove icon
incrementIconReactNode
Custom icon for the increment (+) button.
Default: Material UI Add icon
swapButtonsboolean
When true, swaps both the icons and the behaviour of the two buttons: the left button increments and the right button decrements.
iconButtonPropsIconButtonProps
IconButtonProps forwarded to both stepper IconButtons — custom size, sx, etc. The interaction/accessibility essentials (onClick, onMouseDown, edge, disabled, aria-label) are controlled by the component.
captionReactNode
Content rendered under the value.
E.g. an icon + label.
captionPropsBoxProps
Props forwarded to the Box wrapping caption — e.g. a custom sx to override its position, spacing, font size or color. Merged with the component's own base caption styles rather than replacing them, and accepts any sx form — object, array, or function. Has no effect when caption isn't provided.
labelReactNode
Label displayed for the field. Defaults to a human-readable label derived from fieldName, e.g. firstName becomes "First Name".
showLabelAboveFormFieldboolean
When true, renders the field label above the form field in the FormLabel component, instead of inside or beside it.
formLabelPropsFormLabelProps
FormLabelProps forwarded to the internal FormLabel. The id is managed by the component. Multiple fields can be configured using the ConfigProvider component.
hideLabelboolean
When true, hides the rendered field label while preserving accessible labeling where possible.
requiredboolean
Indicates that the field is mandatory by adding an asterisk symbol (*) to the form label and setting the relevant accessibility attributes.
errorMessagestring | string[]
Validation error for the field — pass a single message string, or a string[] when the field can fail multiple rules at once (every message is shown together). A non-empty string or array puts the field in an error state; undefined/''/[] clear it.
Normalize your form library's error shape to this at the call site (e.g. an RHF FieldError via its .message). Use renderError to customize how the message(s) are rendered.
renderError(errors: string[]) => ReactNode
Custom renderer for the resolved error message(s), called only when the field is in an error state. Always receives a string[] — use errors[0] for the common single-message case, or map over errors when a field fails several rules. By default a single message renders as text and multiple messages render on separate lines.
hideErrorMessageboolean
If true, hides the error message text while keeping the field in an error state.
formHelperTextPropsFormHelperTextProps
FormHelperTextProps forwarded to the internal FormHelperText. The id is managed by the component. Multiple fields can be configured using the ConfigProvider component.
customIds{ field, label, helperText, error }
Overrides the default field, label, helper text, and error IDs used for accessibility.

Playground

Integrated with TanStack Form — covers onlyIntegers, nonNegative, maxDecimalPlaces, stepAmount, min / max bounds, swapButtons, caption/captionProps, with validation surfaced through errorMessage.