Invoice total: $1,250,000.50
Refund: -$42.90
Revenue: $1.2M$1,290,000.00
Conversion: 12%
Weight: 12.5 kg
import { Money } from "@hel/ui/money"
import { NumberText } from "@hel/ui/number-text"
import { Stack } from "@hel/ui/stack"When to use it#
Use Money for every amount of money and NumberText for every other number that people read: a count, a percentage, a weight, a change from last week. They read one setting for the language, digits and currency, so a price in a table, in a dialog and in a chart label look the same, and change together.
Do not build amounts with toFixed or toLocaleString in each place. The commas, decimals and currency symbols drift apart, and a new language means finding every one of them. To let people type an amount, use a Number Field or the amount mask.
Import#
import { Money } from "@hel/ui/money"
import { NumberText } from "@hel/ui/number-text"Usage#
Both are pieces of Text, so they take size, weight and the rest, and they can sit inside a sentence.
<Money amount="1250000.50" />
<Money amount={123456} amountIn="minor" currency="USD" />
<NumberText value={0.1245} format="percent" />
<NumberText value={12.5} format="unit" unit="kilogram" />An amount can be a number, a BigInt or a decimal string. Pass amounts from your API as the strings they come in, because a string keeps every digit and a number can lose some. The element is a <data> tag with the exact value, and a missing or invalid value shows a dash, or the emptyText you give.
Examples#
Currencies and styles#
The currency comes from the config of the app, and currency changes it for one amount. locale changes the language of one amount, amountIn="minor" reads an amount given in cents, and currencyDisplay writes the code or the name instead of the symbol. currencySign="accounting" writes a negative amount in parentheses, in the languages that do that.
US dollars
$1,234.50Euros, in German
1.234,50 €Iraqi dinars
IQDÂ 1,250,000From cents
$1,234.56Currency code
USDÂ 1,234.50Accounting
($1,234.50)import { Flex } from "@hel/ui/flex"
import { Money } from "@hel/ui/money"
import { Stack } from "@hel/ui/stack"Iraqi dinars are a case to watch: the browser writes IQD with no decimals, but the currency has three digits (fils). If your backend sends fils, pass minorUnits={3}, or set it once for the currency in money.currencies in the config.
Gains and losses#
Numbers are plain text by default. tones gives the positive, negative and zero amounts a color, and signDisplay="exceptZero" adds the plus sign, which is what you want for a change. variant="contained" draws the number in a small tinted pill.
Subscriptions
One-time sales
Refunds
import { Flex } from "@hel/ui/flex"
import { Money } from "@hel/ui/money"
import { NumberText } from "@hel/ui/number-text"The tones are neutral, positive, negative and caution, and the theme decides the colors. Color alone should never carry the meaning, so keep the sign or a word next to it. Set the tones once in the config to color every amount in the app, and use tone to force one for a single amount.
Short form#
compact writes $1,290,000 as "$1.2M". It cuts off the extra digits and does not round them up, so the short form never shows more than the real amount. Use compact="auto" with compactOptions.from to shorten only the large ones. On hover the full amount appears, and a screen reader reads the full amount too, never the short one.
Full
$1,290,000.00Short
$1.2M$1,290,000.00Long words
1.2 million1,290,000Only from 10,000 up
Hover a short amount to see the full one.
import { Flex } from "@hel/ui/flex"
import { Money } from "@hel/ui/money"
import { NumberText } from "@hel/ui/number-text"Props#
Money#
| Prop | Type | Default |
|---|---|---|
amount | a number, a BigInt or a decimal string | required |
currency | an ISO 4217 code | from the config, "USD" |
amountIn | "major" | "minor" | "major" |
minorUnits | number | the digits of the currency |
symbol | string | the symbol of the currency |
currencyDisplay | "symbol" | "narrowSymbol" | "code" | "name" | "symbol" |
currencySign | "standard" | "accounting" | "standard" |
NumberText#
| Prop | Type | Default |
|---|---|---|
value | a number, a BigInt or a decimal string | required |
format | "decimal" | "percent" | "unit" | "decimal" |
percentOf | "fraction" | "hundred" | "fraction" |
unit | a unit such as "kilogram" | none |
unitDisplay | "short" | "narrow" | "long" | "short" |
With percentOf="fraction", 0.125 is 12.5%. If your API sends 12.5 for 12.5%, use percentOf="hundred".
Shared by both#
| Prop | Type | Default |
|---|---|---|
locale / numberingSystem | a BCP 47 locale, a digits name | from the config |
compact | boolean | "auto" | from the config |
compactOptions | { from, display, fractionDigits, roundingMode } | from the config |
minimumFractionDigits / maximumFractionDigits | number | from the currency or the locale |
roundingMode | an Intl rounding mode | the browser's |
trailingZeroDisplay | "auto" | "stripIfInteger" | "auto" |
signDisplay | "auto" | "always" | "exceptZero" | "negative" | "never" | "auto" |
tone / tones | a tone, or a tone for each sign | neutral |
variant | "plain" | "contained" | "plain" |
renderParts | (parts, result) => ReactNode | none |
showFullOnHover | boolean | true |
emptyText | ReactNode | a dash |