Skip to content

InputNumber

A numeric input with increment/decrement buttons.

Usage

v-model is a plain number | undefined - an empty field commits as undefined, not 0 or NaN:

vue
<script setup lang="ts">
const value = ref<number>()
</script>

<template>
  <SInputNumber v-model="value" />
</template>

Type a value directly, use the arrow keys while focused, or click the ±buttons - all three commit through the same min/max/step clamping.

Min, max, and step

min/max clamp the committed value and disable the button at whichever boundary is reached; step controls how much each click or arrow-key press changes the value (default 1):

vue-html
<SInputNumber v-model="value" :min="0" :max="10" :step="2" />

wrap changes what happens at the boundary - instead of clamping and disabling the button there, stepping past max cycles to min (and past min cycles to max), the same way an hour-of-day or a compass heading wraps around rather than getting stuck:

vue-html
<SInputNumber v-model="hour" :min="1" :max="12" wrap />

Format options

format-options is a plain Intl.NumberFormatOptions object - the same shape every other JS number-formatting API already uses - applied to the value once it's no longer being edited. Typing itself always shows the plain number, so the two never fight each other:

vue-html
<SInputNumber v-model="value" :format-options="{ minimumIntegerDigits: 2 }" />

Orientation

orientation="vertical" replaces the two full-height flanking buttons with a single compact up/down pair pinned to the field's end edge - useful when horizontal space is tight:

vue-html
<SInputNumber v-model="value" orientation="vertical" />

Sizes and states

size takes sm / md / lg, disabled prevents interaction and dims the control, and invalid switches the ring to the danger role's focus color:

vue-html
<SInputNumber v-model="value" size="sm" />
<SInputNumber v-model="value" disabled />
<SInputNumber v-model="value" invalid />

Colors

color sets the focus-ring color - the resting (unfocused) ring stays --selaras-resolved-border-default regardless, and invalid always wins over a custom color:

vue-html
<SInputNumber v-model="primary" color="primary" />
<SInputNumber v-model="success" color="success" />
<SInputNumber v-model="danger" color="danger" />

Forms integration

Wrap it in FormField to get id/name/ invalid and aria-describedby wired up automatically - invalid and size both fall back to FormField's own state when not set directly.

Custom :ui

To see exactly what you'd be overriding - the current default classes for every slot and variant - here's InputNumber's own theme file:

Theme source "input-number" not found

Props

PropTypeDefault
idstring-
namestring-
modelValuenumber-
minnumber-
maxnumber-
stepnumber1
wrapbooleanfalse
placeholderstring-
formatOptionsIntl.NumberFormatOptions-
localestring-
disabledbooleanfalse
invalidbooleanfalse
color'primary' | 'neutral' | 'secondary' | 'success' | 'danger' | 'info' | 'warning'primary
size'sm' | 'md' | 'lg'md
orientation'horizontal' | 'vertical'horizontal
uiPartial<Record<'root' | 'input' | 'stepper' | 'stepperButton', string | object>>-

Emits

EmitPayloadDescription
update:modelValuenumber | undefinedFires on increment/decrement click, ArrowUp/ArrowDown, or blur/Enter after typing
Edit this page