- Accordion
- Action Bar
- Action Toast
- Alert Banner
- Alert Dialog
- Alert
- Alphabet Index
- Amount Field
- Aspect Ratio
- Attachment
- Availability List
- Avatar
- Badge
- Bank Card
- Breadcrumb
- Bubble
- Button Group
- Button
- Calendar
- Camera Frame
- Card Design Picker
- Card Details
- Card Peek
- Card Stack
- Card
- Carousel
- Chart
- Chat Thread
- Checkbox
- Chip Group
- Choice Tile
- Code Block
- Code Dots
- Collapsible
- Combobox
- Command
- Context Menu
- Copy Field
- Countdown
- Country Select
- Data Table
- Date Field
- Date Picker
- Date Range Picker
- Dialog
- Direction
- Document Paper
- Drawer
- Dropdown Menu
- Empty
- Entity Avatar
- Field
- File Trigger
- File Upload Item
- Hover Card
- Input Group
- Input OTP
- Input
- Item
- Kbd
- Key Value
- Keypad
- Label
- Live Region
- Marker
- Message Scroller
- Message
- Money Amount
- Native Select
- Number Field
- Pagination
- Percent Split Editor
- Phone Field
- Popover
- Progress
- QR Code
- Questionnaire
- Radio Group
- Range Histogram
- Rating
- Resizable
- Scroll Area
- Search Field
- Select
- Separator
- Share Bar
- Sheet
- Sidebar
- Skeleton
- Slider
- Sonner
- Sortable List
- Spinner
- Status Timeline
- Step Header
- Steps List
- Swipe Row
- Switch
- Table
- Tabs
- Text Field
- Textarea
- Tier Ladder
- Toggle Group
- Toggle
- Token Search Field
- Tooltip
- Trace
- Transaction Row
- Usage Meter
"use client"
import * as React from "react"The AmountField component is the main input on a send, top-up or exchange screen. It is built on React Aria NumberField, so it parses and formats the amount for the user's locale, opens a decimal keyboard on phones and validates against minValue and maxValue. Use it for any amount of money the user types freely. For counts and small stepped limits, such as a per-payment limit in steps of $50, use NumberField instead.
Installation#
pnpm dlx shadcn@latest add @dots/amount-field
Usage#
import {
AmountField,
AmountFieldCurrency,
AmountFieldDescription,
AmountFieldGroup,
AmountFieldInput,
AmountFieldLabel,
} from "@/components/ui/amount-field"<AmountField defaultValue={25}>
<AmountFieldLabel>Amount</AmountFieldLabel>
<AmountFieldGroup>
<AmountFieldInput />
<AmountFieldCurrency>USD</AmountFieldCurrency>
</AmountFieldGroup>
<AmountFieldDescription>Balance $1,250.00</AmountFieldDescription>
</AmountField>Composition#
Use the following composition to build an amount field:
AmountField
├── AmountFieldLabel
├── AmountFieldGroup
│ ├── AmountFieldInput
│ └── AmountFieldCurrency or AmountFieldCurrencySelect
├── AmountFieldDescription
├── AmountFieldError
└── AmountFieldPresets
└── AmountFieldPresetUse AmountFieldPair to link two fields by a rate, with a swap button between them:
AmountFieldPair
├── AmountField (variant="inset")
├── AmountFieldSwap
└── AmountField (variant="inset")Features#
- Locale-aware parsing and formatting through React Aria
NumberField, with a decimal keyboard on phones minValuedefaults to0and scroll-wheel changes are off, so the amount never changes by accident- A fixed currency, or a currency pill that opens a list of currencies with their balances
- Quick amount chips that set the amount and show as pressed while it matches
onInputValueChangereports every keystroke, for live results such as the other side of an exchange- An
insetvariant andAmountFieldPairfor two-sided exchanges with a swap button - Validation and an error message read with the field
Currency#
Use AmountFieldCurrency when the currency is fixed. Use AmountFieldCurrencySelect when the user picks the balance the money comes from. Each option can show a balance in description and can be turned off with isDisabled.
"use client"
import * as React from "react"Quick amounts#
Put AmountFieldPresets inside the field and add one AmountFieldPreset per amount. A preset sets the amount when pressed and shows as pressed while the amount equals its value. When the label is a share such as "25%", pass an aria-label that says the amount too.
"use client"
import {<AmountFieldPresets>
<AmountFieldPreset value={312.5} aria-label="25%, $312.50">
25%
</AmountFieldPreset>
<AmountFieldPreset value={1250} aria-label="Max, $1,250.00">
Max
</AmountFieldPreset>
</AmountFieldPresets>Exchange#
Wrap two inset fields in AmountFieldPair and place AmountFieldSwap between them. onChange fires only when the amount is committed, on blur or Enter. Use onInputValueChange to update the other side while the user types. It reports NaN while the field is empty.
"use client"
import * as React from "react"Invalid#
Set isInvalid and render AmountFieldError in place of the description to say what to fix. Offer a way out, such as sending the whole balance.
"use client"
import * as React from "react"Disabled and read-only#
Set isDisabled when the amount cannot be used right now, or isReadOnly when it is set elsewhere. Quick amounts follow the field and turn off in both cases.
"use client"
import {Accessibility#
AmountField is a React Aria NumberField. The input is a text field with numeric input mode. The label, description and error are linked to the input, and the input is marked invalid while isInvalid is set. The error is read when the input gets focus, so move focus to it after a failed check (see below).
Label the field#
Always render AmountFieldLabel, or pass aria-label to AmountField. AmountFieldCurrencySelect adds the field label to its own name, so it is read as "You send, Currency, USD".
Say the amount on share presets#
A preset labeled "25%" does not say how much money it sets. Pass an aria-label with the amount, such as "25%, $312.50".
Focus after a failed check#
AmountFieldInput accepts a ref. When a check on submit fails, move focus back to the input so the user lands on the error.
const inputRef = React.useRef<HTMLInputElement>(null)
<AmountFieldInput ref={inputRef} />
// After a failed check
inputRef.current?.focus()Label the swap button#
AmountFieldSwap is icon-only and is named "Swap currencies" by default. Pass aria-label if it swaps something else.
API Reference#
AmountField#
The root. Accepts all React Aria NumberField props. className takes a string and children takes the parts below.
| Prop | Type | Default | Description |
|---|---|---|---|
variant | "default" | "inset" | "default" | inset puts the label, amount and help text on the surface. |
value | number | - | The amount (controlled). NaN means empty. |
defaultValue | number | - | The amount (uncontrolled). |
onChange | (value: number) => void | - | Called when the amount is committed, on blur or Enter. |
onInputValueChange | (value: number) => void | - | Called on every accepted keystroke with the amount so far. NaN while empty. |
formatOptions | Intl.NumberFormatOptions | { maximumFractionDigits: 2 } | How the amount is formatted and parsed. |
minValue | number | 0 | The smallest amount allowed. |
maxValue | number | - | The largest amount allowed. |
isWheelDisabled | boolean | true | Turns off changing the amount with the scroll wheel. |
isInvalid | boolean | - | Marks the field invalid and shows AmountFieldError. |
isDisabled | boolean | - | Disables the field and its quick amounts. |
isReadOnly | boolean | - | Makes the field read-only and disables its quick amounts. |
className | string | - | Additional classes to apply to the root. |
AmountFieldLabel#
The field label. Renders a Label and accepts all of its props.
| Prop | Type | Default | Description |
|---|---|---|---|
className | string | - | Additional classes to apply to the label. |
AmountFieldGroup#
The surface that holds the amount and the currency. Accepts React Aria Group props.
| Prop | Type | Default | Description |
|---|---|---|---|
className | string | - | Additional classes to apply to the surface. |
AmountFieldInput#
The amount input. Accepts React Aria Input props.
| Prop | Type | Default | Description |
|---|---|---|---|
placeholder | string | "0" | Shown while the field is empty. |
ref | React.Ref<HTMLInputElement> | - | Use it to focus the input, e.g. after an error. |
className | string | - | Additional classes to apply to the input. |
AmountFieldCurrency#
A fixed currency on the surface, such as a code or a symbol. Accepts all <span> props.
| Prop | Type | Default | Description |
|---|---|---|---|
className | string | - | Additional classes to apply to the currency. |
AmountFieldCurrencySelect#
A currency pill that opens a list of currencies. Put it inside AmountFieldGroup. Accepts React Aria Select props except the collection and selection props listed here.
| Prop | Type | Default | Description |
|---|---|---|---|
currencies | AmountFieldCurrencyOption[] | - | The currencies to choose from. Required. |
value | string | null | - | The selected currency code (controlled). |
defaultValue | string | null | - | The selected currency code (uncontrolled). |
onChange | (code: string) => void | - | Called with the code of the picked currency. |
placeholder | string | "Currency" | Shown before a currency is picked. |
aria-label | string | "Currency" | Name of the pill. The field label is added to it. |
disabledKeys | Iterable<Key> | - | Codes to disable, in addition to isDisabled ones. |
className | string | - | Additional classes to apply to the select. |
AmountFieldCurrencyOption:
| Field | Type | Description |
|---|---|---|
code | string | ISO 4217 or token code, e.g. "EUR". Used as the key. |
name | string | e.g. "Euro". |
icon | React.ReactNode | A monochrome flag or coin. Without it the code is shown in a round chip. |
description | React.ReactNode | Quiet text at the end of the row, e.g. the balance. |
isDisabled | boolean | Turns the option off. |
AmountFieldDescription#
Help under the amount, such as the balance or the exchange rate. Read with the field. Accepts React Aria Text props except slot.
| Prop | Type | Default | Description |
|---|---|---|---|
className | string | - | Additional classes to apply to the description. |
AmountFieldError#
Says what to fix. Shown while the field is invalid. Accepts React Aria FieldError props.
| Prop | Type | Default | Description |
|---|---|---|---|
children | React.ReactNode | - | The error message. |
className | string | - | Additional classes to apply to the error. |
AmountFieldPresets#
A row of quick amounts. Put it inside AmountField. Accepts all <div> props.
| Prop | Type | Default | Description |
|---|---|---|---|
aria-label | string | "Quick amounts" | Name of the group. |
className | string | - | Additional classes. |
AmountFieldPreset#
A quick amount. Renders a Toggle and accepts its props except isSelected, defaultSelected and onChange.
| Prop | Type | Default | Description |
|---|---|---|---|
value | number | - | The amount to set. Rounded to the fraction digits the field keeps. Required. |
isDisabled | boolean | - | Turns the preset off. It is also off while the field is disabled or read-only. |
className | string | - | Additional classes to apply to the preset. |
AmountFieldPair#
Stacks two inset amount fields linked by a rate. Accepts all <div> props.
| Prop | Type | Default | Description |
|---|---|---|---|
className | string | - | Additional classes to apply to the pair. |
AmountFieldSwap#
Swaps the two sides of an AmountFieldPair. Renders a Button and accepts its props except children, variant and size.
| Prop | Type | Default | Description |
|---|---|---|---|
onPress | (e: PressEvent) => void | - | Called when the swap is pressed. |
aria-label | string | "Swap currencies" | Name of the button. |
className | string | - | Additional classes. |
On This Page
InstallationUsageCompositionFeaturesCurrencyQuick amountsExchangeInvalidDisabled and read-onlyAccessibilityLabel the fieldSay the amount on share presetsFocus after a failed checkLabel the swap buttonAPI ReferenceAmountFieldAmountFieldLabelAmountFieldGroupAmountFieldInputAmountFieldCurrencyAmountFieldCurrencySelectAmountFieldDescriptionAmountFieldErrorAmountFieldPresetsAmountFieldPresetAmountFieldPairAmountFieldSwap