<script setup lang="ts">
import { ANumberFieldDecrement, ANumberFieldIncrement, ANumberFieldInput, ANumberFieldRoot } from 'akar';
</script>
<template>
<ANumberFieldRoot
id="age"
:min="0"
:default-value="18"
class="inline-flex items-center relative"
>
<div class="ps-1 flex items-center start-0 inset-y-0 absolute">
<ANumberFieldDecrement class="text-sm color-primary font-medium px-2.5 py-1.5 rounded-md inline-flex gap-1.5 transition-colors-280 items-center active:color-primary/75 aria-disabled:(color-primary opacity-75 cursor-not-allowed) disabled:(color-primary opacity-75 cursor-not-allowed) hover:color-primary/75 focus:outline-none focus-visible:(ring-2 ring-primary ring-inset) akar:p-1.5">
<i class="i-lucide:minus shrink-0 size-5" />
</ANumberFieldDecrement>
</div>
<ANumberFieldInput class="text-sm color-text-highlighted px-2.5 py-1.5 pe-9 ps-9 text-center border-0 rounded-md bg-background gap-1.5 w-full ring ring-ring-accented ring-inset transition-colors-280 placeholder:color-text-dimmed focus:outline-none disabled:(opacity-75 cursor-not-allowed) focus-visible:(ring-2 ring-primary ring-inset)" />
<div class="pe-1 flex items-center end-0 inset-y-0 absolute">
<ANumberFieldIncrement class="text-sm color-primary font-medium px-2.5 py-1.5 rounded-md inline-flex gap-1.5 transition-colors-280 items-center active:color-primary/75 aria-disabled:(color-primary opacity-75 cursor-not-allowed) disabled:(color-primary opacity-75 cursor-not-allowed) hover:color-primary/75 focus:outline-none focus-visible:(ring-2 ring-primary ring-inset) akar:p-1.5">
<i class="i-lucide:plus shrink-0 size-5" />
</ANumberFieldIncrement>
</div>
</ANumberFieldRoot>
</template>
pnpm add @internationalized/number
npm install @internationalized/number
bun add @internationalized/number
Import all parts and piece them together.
<script setup>
import { ANumberFieldDecrement, ANumberFieldIncrement, ANumberFieldInput, ANumberFieldRoot } from 'akar';
</script>
<template>
<ANumberFieldRoot>
<ANumberFieldDecrement />
<ANumberFieldInput />
<ANumberFieldIncrement />
</ANumberFieldRoot>
</template>
One benefit of using Akar is its flexibility and low-level control over the components. However, this also means that you may need to manually construct more complex UI elements by combining multiple Akar components together.
If you feel there's a lot of elements that needs to be constructed manually using Akar, consider using Pohon UI instead. It provides a higher-level abstraction over Akar components with pre-defined styles and behaviors that can help you build UIs faster.
Contains all the parts of a number field. An input will also render when used within a form to ensure events propagate correctly.
| Prop | Default | Type |
|---|---|---|
as | 'div' | APrimitiveAsTag | ComponentThe element or component this component should render as. Can be overwritten by |
asChild | booleanChange the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. | |
defaultValue | number | |
disabled | booleanWhen | |
disableWheelChange | booleanWhen | |
formatOptions | Intl.NumberFormatOptionsFormatting options for the value displayed in the number field. This also affects what characters are allowed to be typed by the user. | |
id | stringId of the element | |
invertWheelChange | booleanWhen | |
locale | stringThe locale to use for formatting dates | |
max | numberThe largest value allowed for the input. | |
min | numberThe smallest value allowed for the input. | |
modelValue | number | null | |
name | stringThe name of the field. Submitted with its owning form as part of a name/value pair. | |
readonly | booleanWhen | |
required | booleanWhen | |
step | 1 | numberThe amount that the input value changes with each increment or decrement 'tick'. |
stepSnapping | true | booleanWhen |
| Event | Type |
|---|---|
update:modelValue | [val: number]Event handler called when the value changes. |
| Slot | Type |
|---|---|
modelValue | number |
textValue | string |
| Attribute | Value |
|---|---|
[data-disabled] | Present when disabled |
Input
The input component that renders the text value based on value and format options.
| Prop | Default | Type |
|---|---|---|
as | 'input' | APrimitiveAsTag | ComponentThe element or component this component should render as. Can be overwritten by |
asChild | booleanChange the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. |
| Attribute | Value |
|---|---|
[data-disabled] | Present when disabled |
The button that increases the value.
| Prop | Default | Type |
|---|---|---|
as | 'button' | APrimitiveAsTag | ComponentThe element or component this component should render as. Can be overwritten by |
asChild | booleanChange the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. | |
disabled | boolean |
| Attribute | Value |
|---|---|
[data-disabled] | Present when disabled |
[data-pressed] | Present when pressed |
The button that decreases the value.
| Prop | Default | Type |
|---|---|---|
as | 'button' | APrimitiveAsTag | ComponentThe element or component this component should render as. Can be overwritten by |
asChild | booleanChange the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. | |
disabled | boolean |
| Attribute | Value |
|---|---|
[data-disabled] | Present when disabled |
[data-pressed] | Present when pressed |
All options supported by Intl.NumberFormat are supported, including configuration of minimum and maximum fraction digits, sign display, grouping separators, etc.
<template>
<ANumberFieldRoot
:default-value="5"
:format-options="{
signDisplay: 'exceptZero',
minimumFractionDigits: 1,
}"
>
…
</ANumberFieldRoot>
</template>
You can set formatOptions.style to percent to treat the value as a percentage. You need to set the step to 0.01 manually to allow an appropriate step size in this mode.
<template>
<ANumberFieldRoot
:default-value="0.05"
:step="0.01"
:format-options="{
style: 'percent',
}"
>
…
</ANumberFieldRoot>
</template>
You can set formatOptions.style to currency to treat the value as a currency value. The currency option must also be passed to set the currency code (e.g., USD).
If you need to allow the user to change the currency, you should include a separate dropdown next to the number field. The number field itself will not determine the currency from the user input.
<template>
<ANumberFieldRoot
:default-value="5"
:format-options="{
style: 'currency',
currency: 'EUR',
currencyDisplay: 'code',
currencySign: 'accounting',
}"
>
…
</ANumberFieldRoot>
</template>
Adheres to the Spinbutton WAI-ARIA design pattern.
| Key | Description |
|---|---|
ArrowUp | Increase the value |
ArrowDown | Decrease the value |
PageUp | Increase the value by scale of 10 |
PageDown | Decrease the value by scale of 10 |
Home | Set value to minimum (if |
End | Set value to maximum (if |