The `useMoney` hook takes a [MoneyV2 object](https://shopify.dev/api/storefront/reference/common-objects/moneyv2) and returns a default-formatted string of the amount with the correct currency indicator, along with some of the parts provided by [Intl.NumberFormat](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat).
import {useMoney, ShopifyProvider} from '@shopify/hydrogen-react';
export function App() {
return (
<ShopifyProvider languageIsoCode="EN" countryIsoCode="US">
<UsingMoney />
</ShopifyProvider>
);
}
function UsingMoney() {
const myMoney = {amount: '100', currencyCode: 'USD'};
const money = useMoney(myMoney);
return (
<>
<div>Localized money: {money.localizedString}</div>
<div>Money without trailing zeros: {money.withoutTrailingZeros}</div>
</>
);
}
import {useMoney, ShopifyProvider} from '@shopify/hydrogen-react';
import type {MoneyV2} from '@shopify/hydrogen-react/storefront-api-types';
export function App() {
return (
// @ts-expect-error intentionally missing the rest of the props
<ShopifyProvider countryIsoCode="US" languageIsoCode="EN">
<UsingMoney />
</ShopifyProvider>
);
}
function UsingMoney() {
const myMoney = {amount: '100', currencyCode: 'USD'} satisfies MoneyV2;
const money = useMoney(myMoney);
return (
<>
<div>Localized money: {money.localizedString}</div>
<div>Money without trailing zeros: {money.withoutTrailingZeros}</div>
</>
);
}
`useMoney` must be a descendent of a `ShopifyProvider` component.
The `useMoney` hook takes a [MoneyV2 object](https://shopify.dev/api/storefront/reference/common-objects/moneyv2) and returns a default-formatted string of the amount with the correct currency indicator, along with some of the parts provided by [Intl.NumberFormat](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat). Uses `locale` from `ShopifyProvider`
money: MoneyV2
export function useMoney(money: MoneyV2): UseMoneyValue { const {countryIsoCode, languageIsoCode} = useShop(); const locale = languageIsoCode.includes('_') ? languageIsoCode.replace('_', '-') : `${languageIsoCode}-${countryIsoCode}`; if (!locale) { throw new Error( `useMoney(): Unable to get 'locale' from 'useShop()', which means that 'locale' was not passed to '<ShopifyProvider/>'. 'locale' is required for 'useMoney()' to work`, ); } const amount = parseFloat(money.amount); const { defaultFormatter, nameFormatter, narrowSymbolFormatter, withoutTrailingZerosFormatter, withoutCurrencyFormatter, withoutTrailingZerosOrCurrencyFormatter, } = useMemo(() => { const options = { style: 'currency' as const, currency: money.currencyCode, }; return { defaultFormatter: getLazyFormatter(locale, options), nameFormatter: getLazyFormatter(locale, { ...options, currencyDisplay: 'name', }), narrowSymbolFormatter: getLazyFormatter(locale, { ...options, currencyDisplay: 'narrowSymbol', }), withoutTrailingZerosFormatter: getLazyFormatter(locale, { ...options, minimumFractionDigits: 0, maximumFractionDigits: 0, }), withoutCurrencyFormatter: getLazyFormatter(locale), withoutTrailingZerosOrCurrencyFormatter: getLazyFormatter(locale, { minimumFractionDigits: 0, maximumFractionDigits: 0, }), }; }, [money.currencyCode, locale]); const isPartCurrency = (part: Intl.NumberFormatPart): boolean => part.type === 'currency'; // By wrapping these properties in functions, we only // create formatters if they are going to be used. const lazyFormatters = useMemo( () => ({ original: () => money, currencyCode: () => money.currencyCode, localizedString: () => defaultFormatter().format(amount), parts: () => defaultFormatter().formatToParts(amount), withoutTrailingZeros: () => amount % 1 === 0 ? withoutTrailingZerosFormatter().format(amount) : defaultFormatter().format(amount), withoutTrailingZerosAndCurrency: () => amount % 1 === 0 ? withoutTrailingZerosOrCurrencyFormatter().format(amount) : withoutCurrencyFormatter().format(amount), currencyName: () => nameFormatter().formatToParts(amount).find(isPartCurrency)?.value ?? money.currencyCode, // e.g. "US dollars" currencySymbol: () => defaultFormatter().formatToParts(amount).find(isPartCurrency)?.value ?? money.currencyCode, // e.g. "USD" currencyNarrowSymbol: () => narrowSymbolFormatter().formatToParts(amount).find(isPartCurrency) ?.value ?? '', // e.g. "$" amount: () => defaultFormatter() .formatToParts(amount) .filter((part) => ['decimal', 'fraction', 'group', 'integer', 'literal'].includes( part.type, ), ) .map((part) => part.value) .join(''), }), [ money, amount, nameFormatter, defaultFormatter, narrowSymbolFormatter, withoutCurrencyFormatter, withoutTrailingZerosFormatter, withoutTrailingZerosOrCurrencyFormatter, ], ); // Call functions automatically when the properties are accessed // to keep these functions as an implementation detail. return useMemo( () => new Proxy(lazyFormatters as unknown as UseMoneyValue, { // eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call get: (target, key) => Reflect.get(target, key)?.call(null), }), [lazyFormatters], ); }
The localized amount, without any currency symbols or non-number types from the `Intl.NumberFormat.formatToParts` parts.
The currency code from the `MoneyV2` object.
The name for the currency code, returned by `Intl.NumberFormat`.
The currency narrow symbol returned by `Intl.NumberFormat`.
The currency symbol returned by `Intl.NumberFormat`.
A string returned by `new Intl.NumberFormat` for the amount and currency code, using the `locale` value in the [`LocalizationProvider` component](https://shopify.dev/api/hydrogen/components/localization/localizationprovider).
The `MoneyV2` object provided as an argument to the hook.
All parts returned by `Intl.NumberFormat.formatToParts`.
A string with trailing zeros removed from the fractional part, if any exist. If there are no trailing zeros, then the fractional part remains. For example, `$640.00` turns into `$640`. `$640.42` remains `$640.42`.
A string without currency and without trailing zeros removed from the fractional part, if any exist. If there are no trailing zeros, then the fractional part remains. For example, `$640.00` turns into `640`. `$640.42` turns into `640.42`.