Skip to content
WingoUI
ComponentsTemplatesDocsPricingAI agentsBlogChangelogContact
Components
Buttons & Actions
ButtonButton GroupCopy ButtonDelete ButtonTheme ToggleToggleToggle Group
Inputs
Avatar UploadCalendarCheckboxColor PickerComboboxCountry SelectCron PickerCurrency InputDate PickerDate Range PickerDate Time PickerDuration PickerEmail InputEmoji PickerFile UploadFilter BuilderInputInput GroupListboxMasked InputMention InputMonth PickerMulti SelectNumber InputOTP InputPassword InputPhone InputRadio GroupRatingSearch InputSegmented ControlSelectSignature PadSliderSwitchTag InputTextareaTime PickerTimezone SelectTree Select
Forms
Address InputBooking WidgetCSV ImportFieldForm WizardInline EditInvoice line itemsRich Text EditorSchema Builder
Navigation
Back To TopBounce SidebarBreadcrumbCommand MenuGlobal SearchGooey NavHook SidebarNav MenuNav TabsPaginationProximity SidebarScroll ProgressSidebarStepsTable Of ContentsTabsTopbarUser MenuWorkspace Switcher
Overlays
Alert DialogContext MenuDialogDrawerDropdown MenuFamily DrawerHover CardPopoverShare DialogSheetShortcuts DialogTooltipTour
Feedback
AlertBannerCookie ConsentEmoji ReactionEmpty StateNotification BellNotification CenterProgressRoute ProgressSetup ChecklistSkeletonSpinnerToast
Data Display
Approval FlowAvatarBadgeCalendar ViewCode BlockComment ThreadContact cardCountdownDescription ListDiff ViewerFile ListFile managerFolderGanttJSON ViewerKbdLog ViewerMarkdownPresenceSchedulerTagTimelineTree View
Tables & Lists
Audit LogData TableDeal PipelineEditable GridKanbanListMembers tablePermissions MatrixTableTask ListVirtual List
Charts & Stats
Animated CounterArea chartBar chartDonut chartFunnel chartGaugeGitHub ActivityHeatmap calendarLeaderboardLine chartRadar chartRadial barSparklineStat CardTrackerTrend badge
Layout
AccordionApp ShellCardCollapsibleDetail LayoutEntity HeaderPage HeaderScroll AreaSeparatorSettings LayoutSplit ViewToolbar
Media
Audio PlayerCarouselCSV ViewerDOCX EditorDOCX ViewerExcel ViewerFile SystemImage compareImage CropperImage GalleryImage LightboxLocation MapPDF ViewerPowerPoint ViewerQR CodeStep PlayerVideo Player
AI Kit
Agent Status CardChat MessageChat ThreadChat WidgetConversation ListFluid OrbGrid RevealMatrix OrbModel PickerPrompt InputReasoning IndicatorRule BuilderSuggestion ChipsTool Call TimelineUsage MeterVoice InputWorkflow Canvas
Text & Effects
Background PatternGravity LettersMarqueeTerminalText AnimateText Rotate
Mobile
Action SheetBottom Tab BarFABHaptic ButtonHero HeaderInstall PromptLarge Title HeaderNotification BannerOnboarding CarouselPicker WheelPull To RefreshSlide To ConfirmSticky CTA BarSwipe DeckSwipeable Tabs
Commerce
Cart drawerCategory pickerCheckout stepsCoupon inputFavorite buttonFilter panelListing cardListing gridOrder trackerPricePrice rangePricing tableProduct galleryQuantity stepperReviewsSeller card
Marketing Sections
App Download SectionBlog Cards SectionCTA SectionFAQ SectionFeature GridFeature ShowcaseHero SectionIntegrations SectionLogo CloudMarketing FooterMarketing HeaderMarketing SectionNewsletter SectionPricing SectionStats SectionSteps SectionTeam SectionTestimonials SectionTimeline SectionVideo Section
Blocks
Admin Dashboard PageAdmin ShellAI Chat PageAPI Keys PageAutomation Builder PageCheckout PageCRM Contact PageCRM Pipeline PageError PagesInbox PageInvoice PageKPI DashboardListing Detail PageLogin PageMarketplace Home PageMarketplace Search PageMarketplace ShellMobile App HomeOnboarding FlowOrder History PagePost Listing WizardPricing PageProfile PageRegister PageSettings PageUsers Admin Page
Templates
About PageAcademy PageAccessibility PageAffiliate PageAgency PageAI Product PageAppointment Booking PageBlog PageBlog Post PageBug Bounty PageCareers PageChangelog PageCommunity PageCompare PageContact PageCustomer Story PageCustomers PageDemo Booking PageDevelopers PageDocs Home PageDownload PageEnterprise PageEvents PageExperts PageFAQ PageFeature PageGlossary PageHelp Article PageHelp Center PageHome Services PageImpact PageIntegration Detail PageIntegrations PageJob Detail PageLanding PageLaunch PageLegal PageManifesto PagePortfolio PagePress PageProduct Tour PagePromo PageReal Estate PageReferral PageResearch Report PageRestaurant PageRoadmap PageROI Calculator PageSecurity PageSolutions PageStartups PageStatus PageStorefront PageSwitch PageTemplate Gallery PageTravel PageWaitlist PageYear in Review Page
Hooks & Utilities
AI ChatChart kitCommerceDate utilsFile exportFile typesFiltersForm validationHapticsInvoicingMotion presetsNavigationNumber formatRelative timeRomanian locationsSpreadsheetText matchTonesUIProvideruseClipboarduseConsentuseControllableStateuseCooldownuseDebounceuseKeyboardInsetuseLocaleuseLongPressuseMediaControlsuseMediaQueryuseOverflowItemsusePersistedStateuseScrollEdgesuseScrollStateuseSelectionuseShortcutuseStickToBottom
GalleryTheme

Hooks & Utilities

Commerce

Hooks & Utilities

Commerce TypeScript helper for React

Chart kitDate utils
Live props
Options
locale
currency
fractionDigits
Cart
coupon
freeShippingThreshold1000
taxRate21%
pricesIncludeTax
  1. Home
  2. Components
  3. Hooks & Utilities
  4. Commerce
Pro

Commerce TypeScript helper for React

v1.0.0·Added in 1.0.0

Price formatting and cart math for React shops and marketplaces: prices, discounts, unit prices, coupons and cart totals with VAT.

Change the quantities in the cart with the minus and plus buttons: the subtotal, the savings from sale prices, the coupon, the free-delivery bar, the VAT line and the total all come from one computeCartTotals call and always add up. Pick a coupon to see it apply or explain why it does not (a minimum order shows what is still missing). The strip above shows the same prices through formatPrice, a range, a discount and a price per m² in the chosen locale and currency.

Commerce is a Pro React helper in the React hooks and utilities category, written in TypeScript for React 19. It installs as source with npx wingo-ui@latest add commerce, together with Number format.

Add to a project

Requirements: React 19, in Next.js (App Router or Pages), Vite or React Router. Installation guide · Next.js setup · Vite setup

npx wingo-ui@latest add commerce

Commerce 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 formatPrice from @/lib/commerce.

import { computeCartTotals, formatPrice, RO_VAT_RATE } from "@/lib/commerce"

const totals = computeCartTotals(
  [
    { id: "sofa", price: 850, quantity: 1 },
    { id: "pillows", price: 49.9, quantity: 2, compareAtPrice: 69.9 },
  ],
  { shipping: 19.99, freeShippingThreshold: 1000, taxRate: RO_VAT_RATE, coupons: [{ code: "FALL15", type: "percent", value: 15 }] },
)

formatPrice(totals.total, { locale: "en-US", currency: "USD" }) // "$827.32"
formatPrice(1200, { locale: "ro-RO", currency: "RON" }) // "1.200 lei"

API

  • localedefault "en-US"
    string

    The demo's locale. Every formatter takes options.locale; pass the one from useLocale so the server and the browser agree.

  • currencydefault "USD"
    string

    options.currency: an ISO 4217 code (the lib's own default is "USD"). Printed with the narrow symbol ("$", "€", "lei"), never Intl's "US$" or "RON".

  • fractionDigitsdefault "auto"
    "auto" | number

    options.fractionDigits: auto prints whole amounts without decimals ("$1,200") and the currency's digits otherwise ("$1,284.90"); a number forces that many.

  • coupondefault "none"
    string

    The demo's coupon: a percent (FALL15, capped at 150), a fixed amount (SAVE50), free shipping (FREESHIP) or one with a minimum order (MIN2000).

  • freeShippingThresholddefault 1000
    number | null

    computeCartTotals options.freeShippingThreshold: spend this much on items (after coupons) and delivery is free. 0 or null turns the offer off.

  • taxRatedefault 21
    number

    options.taxRate as a fraction (0.21, exported as RO_VAT_RATE; RO_REDUCED_VAT_RATE is 0.11); the demo shows it in percent. Lines can override it with their own taxRate (the book below is at 11%).

  • pricesIncludeTaxdefault true
    boolean

    options.pricesIncludeTax: true (consumer retail) reports the VAT inside the total; false adds it on top (B2B price lists).

  • formatPrice
    (amount: number | null | undefined, options?: FormatPriceOptions) => string

    The main helper, a price as text: "$1,200", "$1,284.90", "1.284,90 lei" (ro-RO, RON), "489.000 €" (de-DE, EUR); "" for null and NaN. Options: currency (default USD), locale, fractionDigits, currencyDisplay ("narrowSymbol" | "symbol" | "code" | "name" | "none"), notation ("compact" gives "$489K"), signDisplay. Thin and narrow spaces become one no-break space so the server and the browser print the same text.

Parts

  • formatPriceParts

    function

    The pieces of a price for styling, in the locale's order: "$" before "1,284.90" in en-US, "1.284,90" then "lei" after a space in ro-RO. Returns { formatted, amount, sign, currency, currencyPosition, spaced } or null.

  • formatPriceRange

    function

    Two prices as one range joined by a spaced en dash, with the currency written once when it follows the number (ro-RO) and on both ends otherwise (en-US). A missing end prints the other alone.

  • discountPercent

    function

    The whole-percent saving of a sale price against its old price: discountPercent(1199, 1599) is 25. 0 without a higher old price.

    • price*
      number

      The sale price.

    • compareAtPrice*
      number | null | undefined

      The old price.

    • returns
      number

      A whole percent, e.g. 25.

  • formatDiscount

    function

    A discount badge in the locale: formatDiscount(25, "en-US") is "-25%" ("-25 %" in ro-RO); "" for 0.

  • unitPrice

    function

    The price per unit, rounded to cents: unitPrice(489000, 54) is 9055.56. null when the quantity is not positive.

  • formatUnitPrice

    function

    A unit price as text: $489,000 for 54 m² reads "$9,056/m²". Whole units from 100 up, cents below ("$4.90/kg").

    • amount*
      number

      The total price.

    • quantity*
      number

      How many units it covers.

    • unit*
      string

      The unit after the slash: m², kg, sq ft.

    • options
      FormatPriceOptions

      The same options as formatPrice.

  • roundMoney

    function

    Rounds half away from zero without float noise: roundMoney(1.005) is 1.01. Every amount the lib returns goes through it.

  • couponDiscount

    function

    What one coupon takes off, or why it does not apply: { valid: true, amount }, { valid: false, reason: "min-subtotal", missing } or { valid: false, reason: "not-applicable" }. Checking that a code exists and has not expired is the server's job; this is the math.

  • computeCartTotals

    function

    The summary of a cart or checkout: item count, subtotal, savings from sale prices, coupon discounts and rejected coupons, shipping with the free-shipping progress, VAT and total. Every amount is rounded to cents, so the lines always add up to the total.

    • lines*
      CartLineInput[]

      { id, price, quantity, compareAtPrice?, taxRate? } per line.

    • options.couponsdefault "[]"
      Coupon[]

      Applied in order, each on what the ones before it left.

    • options.shippingdefault "null"
      number | null

      The delivery price; null means not chosen yet.

    • options.freeShippingThresholddefault "null"
      number | null

      Spend this much on items (after coupons) and delivery is free.

    • options.taxRatedefault 0
      number

      The VAT rate as a fraction (0.21); 0 leaves out the VAT line.

    • options.pricesIncludeTaxdefault true
      boolean

      true reports the VAT inside the total; false adds it on top.

    • returns
      CartTotals

      itemCount, subtotal, savings, discount, discounts, rejectedCoupons, shipping, freeShippingRemaining, freeShippingProgress, tax, total and more.

  • DEFAULT_CURRENCY

    constant

    The currency every formatter uses when options.currency is left out: "USD".

  • RO_VAT_RATE

    constant

    Romania's standard VAT rate as a fraction: 0.21, in force since Aug 1, 2025.

  • RO_REDUCED_VAT_RATE

    constant

    Romania's reduced VAT rate for food, medicines and books: 0.11.

  • Coupon

    type

    { code, type: "percent" | "fixed" | "shipping", value, minSubtotal?, maxDiscount?, label? }.

  • FormatPriceOptions

    type

    currency, locale, fractionDigits, currencyDisplay, notation and signDisplay, shared by every formatter.

  • CartTotals

    type

    What computeCartTotals returns, every amount in major units (dollars, not cents).

Examples

Product card prices

A sale price, the old price struck through and the discount badge.

Prices by locale

The same amounts through formatPrice in four locales and in compact notation.

Coupon checks

couponDiscount explains a minimum order instead of failing silently.

Unit prices

formatUnitPrice for real estate and groceries.

Dependencies

number-format

Files

  • lib/commerce.ts

FAQ

Is the Commerce helper free?

The preview, props and docs are free; copying the source needs a Pro plan. See plans.

How do I install Commerce?

Run npx wingo-ui@latest add commerce. It also adds the Wingo UI item it builds on (Number format). With shadcn, add the @wingo-ui registry with your API key, then run npx shadcn@latest add @wingo-ui/commerce.

See also

Number formatlibCurrency InputuseLocalehook

Used in

Onboarding FlowMarketplace Home PageOrder trackerCheckout stepsCart drawerDeal PipelinePricing tablePriceCoupon inputFilter panelCSV ImportInvoice line items

and 20 more.

More React hooks and utilities

Date utilslibFile exportlibFile typeslibFilterslibForm validationlibHapticslib
All 36 React hooks and utilities
PreviousChart kitNext Date utils
formatPrice$1,200
cents$1,284.90
range$1,200 – $4,300
discount-25%
per m²$9,056/m²
compact$489K
Your cart3 items
  • Nordic desk lampOak · LED
    $189.90$229.90
    1
  • Flex ergonomic chairBlack · mesh
    $649
    1
  • Atomic HabitsBook · 11% VAT
    $54.90
    1
Add $106.20 more for free shipping
Subtotal$893.80
You save (sale prices)-$40
Shipping$19.99
incl. VAT 21%$154.50
Total$913.79
computeCartTotals(lines, { taxRate: 0.21, pricesIncludeTax: true, freeShippingThreshold: 1000 }).total → 913.79