Price React component for Tailwind CSS v4
v1.0.0Added in 1.0.0
A React price component written the way shops print prices: a smaller currency, a struck old price with a discount chip, Negotiable, Free.
A price is text: it reads in order ("$1,200, Negotiable", "$499, Was $649, 23% off") and nothing in it takes focus, a tap or a key. When the amount changes it fades to the new one, or rolls its digits like an odometer with animate="roll"; with announce a screen reader hears the new total once, never on mount. Under reduced motion the new value simply appears. On a narrow phone the amount never breaks; the old price, the chip and the captions wrap below it.
Price is a Pro React component in the React commerce components category, written in TypeScript for React 19 and Tailwind CSS v4. It installs as source with npx wingo-ui@latest add price, together with Animated Counter, Badge, useLocale, Commerce, Motion presets and 3 more and the npm package motion.
Add to a project
Requirements: React 19 and Tailwind CSS v4, in Next.js (App Router or Pages), Vite or React Router. Installation guide · Next.js setup · Vite setup
npx wingo-ui@latest add pricePrice is part of Pro, with 298 more Pro items: $8 a month or $150 once. 14-day refund before you download any Pro item. See plans
Usage
Import Price from @/components/ui/price.
import { Price } from "@/components/ui/price"
export function ListingPrice() {
return (
<div className="flex flex-col gap-3">
<Price price={320} priceType="negotiable" labels={{ negotiable: "Or best offer" }} />
<Price
price={499}
compareAtPrice={649}
size="lg"
layout="stacked"
labels={{ compare: "Was", discount: (percent) => `Save ${percent}%` }}
/>
<Price price={489000} unit={{ quantity: 580, unit: "sq ft" }} />
<Price price={1900} priceSuffix="/mo" caption="Utilities not included" />
</div>
)
}Props
price*default1200number | nullThe amount in major units (dollars, not cents). null (or NaN) renders labels.onRequest ("Price on request").
currencydefault"USD"stringISO 4217 code, printed with its narrow symbol: "$", "€", "lei". An unknown code prints the number with the code ("1,200 XYZ").
localedefault""stringIntl locale for grouping, decimals and where the currency goes ("$1,200" in en-US, "1.200 €" in de-DE). Empty uses the UIProvider locale, then the page.
fractionDigitsdefault"auto""auto" | "0" | "2" | numberauto prints whole amounts without decimals ("$1,200") and the currency's digits otherwise ("$1,284.90"); a number (or "0" / "2") forces that many.
compareAtPricedefault0number | nullThe old price. Shown struck through, with the sale color and the discount chip, only when it is above price (and above 0); 0 or null means none.
priceTypedefault"fixed""fixed" | "negotiable" | "free" | "exchange" | "on-request"negotiable adds labels.negotiable after the amount; free, exchange and on-request replace the amount with their label (free in the success color).
priceSuffixdefault""stringMuted text after the currency: "/mo", "/night", "/each".
unit{ quantity: number; unit: string }Adds the price per unit to the caption: $489,000 for 580 sq ft prints "$843/sq ft".
captiondefault""ReactNodeOne muted line under the price ("Tax included", "+ $9.99 shipping"), after the unit price when both are set.
showDiscountdefaulttruebooleanThe "-25 %" chip next to the old price when on sale. Screen readers hear labels.discount instead of the chip.
saleTonedefault"danger""neutral" | "brand" | "success" | "warning" | "danger" | "info"The sale amount's color and the chip's tone. A markdown is a status, so it defaults to danger; the two prices still say it without color.
sizedefault"md""sm" | "md" | "lg" | "xl"14 / 17 / 22 / 32px amounts (xl grows to 40px from 768px): a cart line, a card, a detail page, a pricing hero.
currencyStyledefault"small""small" | "inline"small sets the currency at 0.72em of the amount, in the locale's order; inline prints it at the same size.
layoutdefault"inline""inline" | "stacked"inline keeps everything on one line and wraps between parts; stacked puts the old price, the chip and Negotiable on a second line (cards, plans).
animatedefault"fade""none" | "fade" | "roll"How a new amount appears: a quick keyed fade, digits rolling like an odometer (totals, plans), or nothing. Reduced motion always swaps instantly.
announcedefaultfalsebooleanReads a changed price out politely through a live region (cart and checkout totals). Never on mount.
labelsPartial<PriceLabels>Copy: negotiable "Negotiable", free "Free", exchange "Swap", onRequest "Price on request", compare "Was" (sr-only before the old price), discount (percent) => "25% off" (sr-only when the chip shows). Pass only the keys you translate.
classNamesPartial<Record<"current" | "amount" | "currency" | "suffix" | "label" | "compare" | "discount" | "negotiable" | "caption", string>>Extra classes for the inner parts.
classNamestringExtra classes for the root span, merged last. Other span props (id, aria-*, data-*) spread onto it.
Parts
PRICE_SIZES
constantThe type classes of each size (amount, compare, caption and the badge size), for bars and tables that print a price without the component but should match it.
DEFAULT_PRICE_LABELS
constantThe English copy: negotiable "Negotiable", free "Free", exchange "Swap", onRequest "Price on request", compare "Was" and discount "25% off".
Examples
Sale in every size
An old price struck through with its discount chip, inline and stacked.
Listing types
Negotiable after the amount; free, swap and on request instead of it.
Real estate
The price per unit of area as a caption, in dollars and in euros.
Rent and stays
A muted suffix after the currency, with a brand sale tone.
A changing total
Digits roll as the quantity changes and the new total is announced.
Locales
Grouping, decimals and the currency's side follow the locale.
B2B caption
A muted line under the amount for prices before tax.
Files
- components/ui/price.tsx
FAQ
Is the React Price component free?
The preview, props and docs are free; copying the source needs a Pro plan. See plans.
How do I install Price?
Run npx wingo-ui@latest add price. It also adds the Wingo UI items it builds on (Animated Counter, Badge, useLocale, Commerce, Motion presets and 3 more) and the npm package motion. With shadcn, add the @wingo-ui registry with your API key, then run npx shadcn@latest add @wingo-ui/price.
