Componentpickers0.87 KB gzipZero dependencies
Masked Currency
Formatted currency price input with prefix badge and numeric mask.
Preview
Live Preview
$
Live Stub Balance: $85.00Installation
Tokens guide →Add to your project via CLI (zero dependencies, code you own):
npx bigbullui add masked-currencyOr install the full npm package:
npm install bigbulluiUsage
Open in StackBlitzimport { MaskedCurrency } from "@/components/ui/masked-currency";
<MaskedCurrency currency="$" defaultValue={4500} onValueChange={(cents) => console.log(cents)} />Usage guidance
Use Masked Currency when a ticket stub surface needs Formatted currency price input with prefix badge and numeric mask. Keep props minimal and prefer composition with sibling stubs.
Avoid Masked Currency for long form flows or dense data grids where a dedicated layout block fits better.
Source code
masked-currency.tsxZero dependencies (only React + utils). Copy and paste directly into your project:
"use client";
import * as React from "react";
import { cn } from "./lib/utils";
export interface MaskedCurrencyProps
extends Omit<React.InputHTMLAttributes<HTMLInputElement>, "onChange"> {
currency?: string;
value?: number;
defaultValue?: number;
onValueChange?: (cents: number, formatted: string) => void;
className?: string;
}
export function MaskedCurrency({
currency = "$",
value: controlledValue,
defaultValue = 4500, // in cents (e.g. 4500 = $45.00)
onValueChange,
className,
...props
}: MaskedCurrencyProps) {
const [internalCents, setInternalCents] = React.useState(defaultValue);
const isControlled = controlledValue !== undefined;
const currentCents = isControlled ? controlledValue : internalCents;
const formatCents = (cents: number) => {
const dollars = (cents / 100).toLocaleString("en-US", {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
});
return dollars;
};
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const rawDigits = e.target.value.replace(/[^0-9]/g, "");
const cents = parseInt(rawDigits || "0", 10);
if (!isControlled) setInternalCents(cents);
onValueChange?.(cents, `${currency} ${formatCents(cents)}`);
};
return (
<div
className={cn(
"relative inline-flex items-center rounded-lg border-2 border-foreground bg-card shadow-xs font-mono select-none",
className
)}
>
<span className="flex h-full items-center border-r-2 border-dashed border-border bg-secondary/70 px-3 text-xs font-bold text-muted-foreground">
{currency}
</span>
<input
type="text"
inputMode="numeric"
value={formatCents(currentCents)}
onChange={handleChange}
className={cn(
"w-full bg-transparent px-3 py-2 text-right text-xs font-bold text-foreground outline-none",
"focus-visible:ring-1 focus-visible:ring-accent"
)}
{...props}
/>
</div>
);
}
Props
| Prop | Type | Description |
|---|---|---|
| currency? | string | |
| value? | number | |
| defaultValue? | number | |
| onValueChange? | (cents: number, formatted: string) => void | |
| className? | string |