f-ui
Components

Currency Input

Decimal money field with optional symbol, formatted read-only display, and pure parse/format helpers from the currency-format registry item.

Formatting lives in currency-format (no UI). currency-input is the field component and declares registryDependencies including input and the currency-format registry item (via URL in the manifest) so the CLI installs helpers when you add the input.

Currency Format

Pure parseMoneyNumber and formatMoneyForDisplay — no npm dependencies. Use in tables, summaries, or custom read-only UI.

Installation

pnpm dlx shadcn@latest add https://ui.isaacfei.com/r/currency-format.json
npx shadcn@latest add https://ui.isaacfei.com/r/currency-format.json
yarn dlx shadcn@latest add https://ui.isaacfei.com/r/currency-format.json
bun x shadcn@latest add https://ui.isaacfei.com/r/currency-format.json

With a namespace: npx shadcn@latest add @f-ui/currency-format.

Usage

import {
  formatMoneyForDisplay,
  parseMoneyNumber,
} from '@/components/f-ui/currency-format/currency-format';

Example

Currency style (ISO code)

$1,234.56

Decimal style (no code)

42.50

Empty parse — helpers return null / display "-"; in tables you often pair with EmptyValuePlaceholder instead

parseMoneyNumber(""):null

Placeholder:

"use client";

import {
  formatMoneyForDisplay,
  parseMoneyNumber,
} from "@/components/f-ui/currency-format/currency-format";
import { EmptyValuePlaceholder } from "@/components/f-ui/empty-value-placeholder";

export function CurrencyFormatDemo() {
  const usd = formatMoneyForDisplay(1234.56, "USD", "en-US");
  const plain = formatMoneyForDisplay(42.5, undefined, "en-US");
  const emptyParse = parseMoneyNumber("");

  return (
    <div className="max-w-md space-y-4 text-sm">
      <div className="space-y-1">
        <p className="text-muted-foreground text-xs">Currency style (ISO code)</p>
        <p className="font-mono tabular-nums">{usd}</p>
      </div>
      <div className="space-y-1">
        <p className="text-muted-foreground text-xs">Decimal style (no code)</p>
        <p className="font-mono tabular-nums">{plain}</p>
      </div>
      <div className="space-y-1">
        <p className="text-muted-foreground text-xs">
          Empty parse — helpers return null / display &quot;-&quot;; in tables you often pair
          with <code className="text-foreground">EmptyValuePlaceholder</code> instead
        </p>
        <p className="flex flex-wrap items-center gap-2">
          <span className="text-muted-foreground">parseMoneyNumber(&quot;&quot;):</span>
          <span className="font-mono">{emptyParse === null ? "null" : String(emptyParse)}</span>
        </p>
        <p className="flex flex-wrap items-center gap-2">
          <span className="text-muted-foreground">Placeholder:</span>
          <EmptyValuePlaceholder />
        </p>
      </div>
    </div>
  );
}

API Reference

  • parseMoneyNumber(value): returns a finite number or null; accepts numbers, bigints, or strings with grouping / locale-style separators.
  • formatMoneyForDisplay(value, currencyCode?, locale?): formatted string, or "-" when the value cannot be parsed. With a currency code, uses Intl.NumberFormat currency style (two decimals). Without a code, two decimal places, no grouping, no currency symbol.

Currency Input

Controlled decimal field (inputMode="decimal") with optional currency symbol prefix and a read-only branch that formats via formatMoneyForDisplay unless you pass formatReadOnly.

Installation

pnpm dlx shadcn@latest add https://ui.isaacfei.com/r/currency-input.json
npx shadcn@latest add https://ui.isaacfei.com/r/currency-input.json
yarn dlx shadcn@latest add https://ui.isaacfei.com/r/currency-input.json
bun x shadcn@latest add https://ui.isaacfei.com/r/currency-input.json

Namespace: npx shadcn@latest add @f-ui/currency-input — pulls currency-format via registryDependencies when using the registry manifest.

Usage

import { CurrencyInput } from '@/components/f-ui/currency-input/currency-input';

<CurrencyInput
  currencySymbol="USD"
  value={amount}
  onChange={(e) => setAmount(e.target.value)}
/>

<CurrencyInput readOnly value={amount} currencySymbol="USD" />

When readOnly is set, the default display uses formatMoneyForDisplay (treat currencySymbol as an ISO code when appropriate). Pass formatReadOnly to replace that branch entirely.

Example

Editable

USD

Current value:1234.56

Read-only display

$1,234.56
"use client";

import { useState } from "react";

import { CurrencyInput } from "@/components/f-ui/currency-input/currency-input";
import {
  EmptyValuePlaceholder,
  isEmptyDisplayValue,
} from "@/components/f-ui/empty-value-placeholder";

export function CurrencyInputDemo() {
  const [amount, setAmount] = useState("1234.56");

  return (
    <div className="max-w-sm space-y-3">
      <div className="space-y-1">
        <p className="text-muted-foreground text-xs">Editable</p>
        <CurrencyInput
          currencySymbol="USD"
          value={amount}
          onChange={(e) => setAmount(e.target.value)}
          aria-label="Amount"
        />
      </div>
      <p className="text-muted-foreground flex flex-wrap items-center gap-x-1 gap-y-0.5 text-xs">
        <span>Current value:</span>
        {isEmptyDisplayValue(amount) ? (
          <EmptyValuePlaceholder />
        ) : (
          <span className="text-foreground font-medium tabular-nums">{amount}</span>
        )}
      </p>
      <div className="space-y-1">
        <p className="text-muted-foreground text-xs">Read-only display</p>
        <CurrencyInput readOnly value={amount} currencySymbol="USD" />
      </div>
    </div>
  );
}

API Reference

  • currencySymbol: optional string — prefix in editable mode; passed into default read-only formatting.
  • readOnly: bordered display row instead of <input>; empty values show "-" from the formatter unless formatReadOnly handles them.
  • formatReadOnly: (value: string | number | undefined) => string — overrides default formatMoneyForDisplay for read-only display.
  • containerClassName: class on the outer wrapper (both modes).
  • Other Input-like props apply in editable mode; type is text with inputMode="decimal".

On this page