Appearance
NumberInput
A number input field with optional slider, percent mode, and validation.
Examples
Basic
vue
<script setup>
import { ref } from "vue";
const days = ref(10);
</script>
<NumberInput v-model="days" label="Days" placeholder="Number of days" />With hint and validation
vue
<NumberInput
v-model="population"
label="Population"
hint="Total number of individuals"
:min="1000"
:max="100000"
:step="1"
/>Percent mode
vue
<NumberInput v-model="coverage" label="Vaccination coverage" percent :max="1" />Slider
vue
<NumberInput
v-model="r0"
label="R0"
hint="Basic reproduction number"
:step="0.1"
:min="1"
:max="18"
slider
/>Range slider
Bind v-model:range with a [low, high] tuple to render a two-handle slider. Range mode is enabled automatically by the binding — there's no explicit toggle prop.
vue
<script setup>
import { ref } from "vue";
const ageRange = ref([18, 65]);
</script>
<NumberInput
v-model:range="ageRange"
label="Age range"
:min="0"
:max="100"
number-type="integer"
/>Range slider with split bindings
When your state stores the bounds in separate refs (rather than as a tuple), bind them directly with v-model:lower and v-model:upper. You can bind either pair or combine them with v-model:range — writes from the component go to every bound sink.
vue
<script setup>
import { ref } from "vue";
const minAge = ref(18);
const maxAge = ref(65);
</script>
<NumberInput
v-model:lower="minAge"
v-model:upper="maxAge"
label="Age range"
:min="0"
:max="100"
number-type="integer"
/>Range mode works with percent and live as well:
vue
<NumberInput
v-model:range="coverageRange"
label="Coverage range"
percent
live
:max="1"
/>Custom display format
Pass format to control how the value is displayed in the text input and in slider thumb/min/max labels. Accepts a NumberFormat — a preset name (optionally with a :N digits suffix, e.g. "percent:1"), a printf-style format string ("%.2f"), or a (value: number) => string function. The internal model stays a number — only the displayed text changes.
When unset, the default formatting follows the percent and decimals props. When set, format overrides both. Formats that add suffixes or scale the value (e.g. "percent:1" → "12.3%") may not round-trip through the text input — pair them with percent: true for value scaling and use format for display shaping.
The older slider-display prop (a (value: number) => string function that only affected slider thumb/min/max labels) is deprecated but still honored when format is unset. Prefer format for new code.
vue
<script setup>
import { ref } from "vue";
const dayMs = 24 * 60 * 60 * 1000;
const dateStart = Date.UTC(2024, 0, 1);
const dateEnd = Date.UTC(2024, 11, 31);
const dateRange = ref([Date.UTC(2024, 2, 1), Date.UTC(2024, 8, 30)]);
const formatDate = (ms) =>
new Date(ms).toLocaleDateString("en-US", { month: "short", day: "numeric" });
</script>
<NumberInput
v-model:range="dateRange"
label="Date range"
:min="dateStart"
:max="dateEnd"
:step="dayMs"
:format="formatDate"
/>Live slider
With live, the model updates while dragging the slider thumb rather than only on release.
vue
<NumberInput
v-model="coverage"
label="Vaccination coverage"
percent
slider
live
:max="1"
/>Live input
With live on a regular input, the model updates as you type (debounced 300ms). Arrow keys and spinner buttons commit immediately.
vue
<NumberInput v-model="days" label="Days" live />Integer type
With number-type="integer", decimal values are truncated to whole numbers on commit. When combined with percent, the display value (e.g. 42%) is treated as the integer — so internal values like 0.42 are valid.
vue
<NumberInput v-model="days" label="Steps" number-type="integer" />Decimal places
Display precision is inferred from step (e.g. step="0.001" in percent mode shows tenths of a percent). Set decimals explicitly to override.
vue
<NumberInput
v-model="coverage"
label="Coverage"
percent
:step="0.001"
:max="1"
/>
<NumberInput v-model="r0" label="R0" :decimals="3" :min="0" :max="18" />Required
With required, clearing the field shows a validation error on commit.
vue
<NumberInput v-model="days" label="Days" required />Combine required with live to validate as the user types (debounced).
vue
<NumberInput v-model="days" label="Days (on blur)" required />
<NumberInput v-model="days" label="Days (live)" required live />Hidden label
Use hide-label to visually hide the label while keeping it available to screen readers. Useful when a heading or surrounding context already describes the input visually.
vue
<NumberInput v-model="days" label="Days" hide-label />Model
| Name | Type |
|---|---|
v-model | number |
v-model:range | NumberRange |
v-model:lower | number |
v-model:upper | number |
Props
| Prop | Type | Required | Default |
|---|---|---|---|
label | string | No | — |
hideLabel | boolean | No | — |
placeholder | string | No | — |
step | number | No | — |
min | number | No | — |
max | number | No | — |
hint | string | No | — |
percent | boolean | No | — |
slider | boolean | No | — |
live | boolean | No | — |
numberType | "integer" | "float" | No | — |
required | boolean | No | — |
decimals | number | No | — |
percent | 1") may not round-trip through the text input — use | ||
// percent: true for value scaling and format for display shaping. | |||
| format?: NumberFormat` | Yes | — | |
sliderDisplay | (value: number) => string | No | — |