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

Commerce

Checkout steps

Commerce

Checkout steps React component for Tailwind CSS v4

Category pickerCoupon input
Live props
State
defaultStep
Behavior
invoice
requireTerms
freeShippingThresholdNo offer
taxRateNo VAT row
pricesIncludeTax
country
Content
showCoupon
currency
locale
Layout
summary
stepper
fullScreenOnMobile
Appearance
tone
color
size
radius
Code
import { CheckoutSteps } from "@/components/ui/checkout-steps"

<CheckoutSteps />
  1. Home
  2. Components
  3. Commerce
  4. Checkout steps
Pro

Checkout steps React component for Tailwind CSS v4

v1.0.0·Added in 1.0.0

A React multi-step checkout: address and invoice, delivery with lockers, payment and a review that places the order, beside a summary.

Four steps under a stepper: Delivery details (email, a saved address card or a new address with county select, city combobox, street and postal code, then an Individual / Company switch that opens the company fields), Shipping (priced courier, easybox and pickup cards; an easybox opens a searchable list of pickup points), Payment (card, cash on delivery with its fee, bank transfer with the IBAN to copy, instalments that unlock above a total) and Review (every choice with an Edit link, notes, the terms box and Place order with the total). Continue checks the step: the fields that are wrong turn red with a message, the button shakes and focus jumps to the first one; each message clears as you fix it. A saved address missing a field shows Complete the address and opens the form. Changing the county clears the city. Choices that change the price (a coupon, a method that becomes free past the threshold, a payment fee) update the cards and the summary in place, and the new total is read out. Place order spins while the order is sent, turns into a check with a small bounce and locks the review, then the success content fades in and takes focus; a refusal shakes the button and shows the reason above it. The summary is a sticky column when the checkout is wide and a bar at the top when it is narrow (Show summary with the total) that unfolds the lines, the promo code and the totals. On a phone each step is a full-height page: the step count and title on top, the total, Back and Continue at the bottom above the home indicator, hidden while the keyboard is up. Keyboard: Tab through the fields, Enter moves to the next field and continues from the last one, arrows move between radio cards and pickup points, Space picks, mod+Enter in the notes continues; completed steps in the stepper are buttons. Under reduced motion steps and panels fade instead of sliding.

Checkout steps 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 checkout-steps, together with Alert, Badge, Button, Cart drawer, Checkbox and 18 more and the npm packages motion and lucide-react.

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 checkout-steps

Checkout steps 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 CheckoutSteps from @/components/ui/checkout-steps.

import { CheckoutSteps, type DeliveryMethod, type PaymentMethod } from "@/components/ui/checkout-steps"
import type { CartItem } from "@/components/ui/cart-drawer"
import { RO_VAT_RATE } from "@/lib/commerce"
import { cityOptions, countyOptions } from "@/lib/ro-locations"

const items: CartItem[] = [
  { id: "case", title: "iPhone 14 case, silicone, Midnight", price: 79.9, quantity: 2, image: "/demo/phone.jpg" },
  { id: "stand", title: "Aluminum laptop stand", price: 149, quantity: 1, image: "/demo/laptop.jpg" },
]

const delivery: DeliveryMethod[] = [
  { id: "fan", label: "Fan Courier", price: 19.99, eta: "1-2 business days", type: "courier" },
  { id: "pickup", label: "Pick up in Cluj-Napoca", price: 0, type: "pickup" },
]

const payment: PaymentMethod[] = [
  { id: "card", label: "Card" },
  { id: "cash", label: "Cash on delivery", fee: 4.99 },
]

export function Checkout() {
  return (
    <CheckoutSteps
      items={items}
      regions={countyOptions()}
      getCities={(county) => cityOptions(county)}
      deliveryMethods={delivery}
      paymentMethods={payment}
      freeShippingThreshold={250}
      taxRate={RO_VAT_RATE}
      tone="brand"
      onPlaceOrder={(value, totals) =>
        fetch("/api/orders", { method: "POST", body: JSON.stringify({ value, total: totals.total }) })
      }
      successContent={<p>Order #1042 is placed.</p>}
      labels={{
        review: "Confirm",
        placeOrder: (total) => `Pay ${total}`,
      }}
    />
  )
}

Props

  • items*
    CartItem[]

    The cart lines (CartItem from cart-drawer): id, title, price, quantity, compareAtPrice, taxRate, image, variant. Unavailable lines and quantity 0 are left out; an empty list shows the empty state.

  • value
    CheckoutValue

    Controlled checkout data: email, addressId, address (CheckoutAddress), invoice (CheckoutInvoice), deliveryMethod, lockerId, paymentMethod, notes, acceptTerms, coupons (AppliedCoupon[]). Pair it with onValueChange.

  • defaultValuedefault "{}"
    CheckoutValue

    The starting data while uncontrolled (a returning buyer's email, a remembered method).

  • onValueChange
    (value: CheckoutValue) => void

    Called with the whole value on every change.

  • defaultStepdefault "address"
    "address" | "delivery" | "payment" | "review"

    The step it opens on while uncontrolled; the steps before it count as done.

  • step
    CheckoutStepId

    The controlled step; pair it with onStepChange.

  • onStepChange
    (step: CheckoutStepId) => void

    Called when the step changes (Continue, Back, Edit links, the stepper).

  • stepsdefault "["address", "delivery", "payment", "review"]"
    CheckoutStepId[]

    The steps in order, e.g. ["payment", "review"] for a digital product. A step whose data is missing (delivery without deliveryMethods) is skipped with a warning in development.

  • savedAddressesdefault "[]"
    CheckoutAddress[]

    Address cards to pick from, plus New address. The first one is chosen at the start; one missing a required field shows Complete the address and opens the form.

  • regions
    { value: string; label: string; description?: string }[]

    The County select (countyOptions() from ro-locations); without it the county is a text field.

  • getCities
    (region: string) => (CheckoutCity[] | Promise<CheckoutCity[]>)

    The City combobox for the chosen county ({ value, label, description, keywords }[]), sync or async (cityOptions(county) from ro-locations); without it a text field. Typed names outside the list are kept.

  • deliveryMethods
    DeliveryMethod[]

    Required when the delivery step is used: { id, label, description, price, eta, type: courier | locker | pickup, icon, logo, freeFrom, disabled }. Prices turn to Free in place once the threshold or a coupon makes them free.

  • lockers
    CheckoutLocker[] or (methodId: string) => Promise<CheckoutLocker[]>

    The pickup points of a locker method ({ id, label, address, distance, hours }), or a loader called per method (skeleton rows while it runs). A locker method then needs a chosen point.

  • paymentMethods
    PaymentMethod[]

    Required when the payment step is used: { id, label, description, icon, logos, fee, minTotal, disabled, details }. fee joins the summary and the total; below minTotal the card is disabled with Available from; details (a card form slot, the IBAN) unfold under the cards while chosen.

  • invoicedefault true
    boolean

    Shows the Individual / Company switch and the company fields (name, CUI checked with the ANAF key, trade register no.).

  • requireTermsdefault true
    boolean

    Shows the terms checkbox in the review step and requires it before the order is placed.

  • termsLabeldefault "labels.terms"
    ReactNode

    The terms checkbox label, with links to the terms and privacy pages.

  • showCoupondefault true
    boolean

    The promo code field in the summary.

  • couponProps
    Partial<CouponInputProps>

    Passed to the CouponInput: the coupons catalogue, onApply for a server check, labels.

  • freeShippingThresholddefault 0
    number

    Courier and locker delivery is free from this much (items after coupons); a method's freeFrom wins. 0: no offer.

  • taxRatedefault 0
    number

    The VAT rate as a fraction (0.21); 0 hides the VAT row. Lines can carry their own rate.

  • pricesIncludeTaxdefault true
    boolean

    Prices include VAT (Romanian B2C); false adds VAT on top as its own row.

  • summarydefault "aside"
    "aside" | "collapsible" | "none"

    aside: a sticky summary column once the checkout itself is 896px wide (a container query), a Show summary bar when narrower; collapsible: the bar at every width; none: no summary.

  • stepperdefault "horizontal"
    "horizontal" | "progress"

    The form-wizard header from 768px: numbered steps, or a progress bar with the step title. Phones always show the compact progress.

  • fullScreenOnMobiledefault true
    boolean

    Below 768px each step fills the container: the summary bar and step count on top, a sticky footer with the total and Continue above the home indicator.

  • onPlaceOrder
    (value: CheckoutValue, totals: CartTotals) => (void | Promise<unknown>)

    Places the order after the review step validates; totals.total includes the payment fee. A promise shows the button loading; a resolve turns it into a check, then successContent; a rejection shows an alert with its message in the review step.

  • successContent
    ReactNode

    Replaces the wizard once the order is placed (order number, next steps).

  • tonedefault "neutral"
    "neutral" | "brand" | "success" | "warning" | "danger" | "info"

    Continue, Place order and the stepper's active marker.

  • colordefault ""
    string

    Any CSS color for the same parts; replaces the tone.

  • sizedefault "md"
    "sm" | "md"

    Fields, cards, the stepper and the buttons: sm for a narrow column, md for the page.

  • radiusdefault "md"
    "none" | "sm" | "md" | "lg" | "full"

    The corners of Back, Continue and Place order.

  • countrydefault "RO"
    string

    RO checks the postal code (6 digits) and the CUI control key and starts the phone at +40; other codes relax those checks.

  • currencydefault "RON"
    string

    ISO 4217 code for every amount.

  • localedefault ""
    string

    Locale for prices and numbers; empty uses the UIProvider's, then the browser's.

  • labels
    Partial<CheckoutStepsLabels>

    Every string, English by default (DEFAULT_CHECKOUT_STEPS_LABELS): the step titles address "Delivery details", delivery "Shipping", payment "Payment", review "Review"; continue, back, placeOrder(total) "Place order · RON 1,284.90", placed, stepOf(i, n), edit, newAddress, incompleteAddress, contact, the field labels (name, phone, email, county, city, street, details, postalCode, companyName, cui, regCom) and placeholders (emailPlaceholder, countyPlaceholder, cityPlaceholder, cuiPlaceholder, regComPlaceholder), invoiceTitle, person, company, locker, lockerSearch, free, fee(amount), minTotal(amount), paymentFee(method), notes, terms, summary, showSummary(total), hideSummary, the summary rows (subtotal, shipping, total...) and the errors (required, invalidEmail, invalidPhone, invalidCui, invalidPostalCode, acceptTerms, chooseDelivery, chooseLocker, choosePayment).

  • classNames
    Partial<Record<"layout" | "wizard" | "summary" | "summaryBar" | "address" | "delivery" | "payment" | "review" | "placeOrder", string>>

    Extra classes for the inner parts.

  • className
    string

    Extra classes for the root, merged last.

Parts

  • isValidCui

    function

    The Romanian tax ID check: an optional "RO" prefix, 2 to 10 digits, and the ANAF control digit (key 753217532). isValidCui("RO12345674") is true, "RO12345678" is false. Signature: (cui: string) => boolean.

  • validateCheckoutStep

    function

    The same checks the wizard runs, for a server or a custom flow: field errors keyed "email", "address.phone", "invoice.cui", "deliveryMethod", "lockerId", "paymentMethod", "acceptTerms". Options: country, invoice, requireTerms, savedAddresses, deliveryMethods, lockers, paymentMethods, total, labels. Signature: (step: CheckoutStepId, value: CheckoutValue, options?: CheckoutValidationOptions) => Record<string, string>.

  • DEFAULT_CHECKOUT_STEPS_LABELS

    constant

    The English strings, to spread under a translation. Type: CheckoutStepsLabels.

  • CheckoutValue

    type

    The checkout state: email, addressId or address, invoice, deliveryMethod, lockerId, paymentMethod, notes, acceptTerms and coupons.

  • CheckoutAddress

    type

    A saved or new address: id, label ("Home"), name, phone, email, county, city, street, details and postalCode.

  • DeliveryMethod

    type

    A shipping card: id, label, description, price, eta, type ("courier" | "locker" | "pickup"), icon or logo, freeFrom and disabled.

  • CheckoutLocker

    type

    A pickup point in the locker list: id, label, address, distance and hours.

  • PaymentMethod

    type

    A payment card: id, label, description, icon, logos, fee (added while chosen), minTotal (available from this total), disabled and details shown while chosen.

  • CheckoutStepId

    type

    "address" | "delivery" | "payment" | "review", for steps and step.

Examples

Company invoice

A guest orders for Carpathia Trading SRL: Continue flags the tax ID RO12345678; RO12345674 passes the Romanian CUI check.

Easybox

A locker method opens a searchable list of pickup points, loaded from the courier; free from 250 RON.

Payment declined

onPlaceOrder rejects: the button stops spinning and the bank's message shows in the review step.

Progress stepper, brand

stepper="progress" and tone="brand" for Continue and Place order, VAT 21% included.

Digital product

steps={["payment", "review"]}: a subscription needs no address or delivery.

Another country, EUR

country="DE" relaxes the postal code and tax ID checks: en-US and EUR, size="sm", no invoice switch, the summary as a bar.

Phone

Each step is a full-height page with the summary bar on top and the total next to Continue.

Dependencies

motionlucide-reactalertbadgebuttoncart-drawercheckboxcomboboxcoupon-inputform-wizardinputphone-inputpriceradio-groupsegmented-controlselecttextareause-controllable-stateuse-keyboard-insetuse-localecommercemotionnumber-formattonesui-config

Files

  • components/ui/checkout-steps.tsx

FAQ

Is the React Checkout steps component free?

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

How do I install Checkout steps?

Run npx wingo-ui@latest add checkout-steps. It also adds the Wingo UI items it builds on (Alert, Badge, Button, Cart drawer, Checkbox and 18 more) and the npm packages motion and lucide-react. With shadcn, add the @wingo-ui registry with your API key, then run npx shadcn@latest add @wingo-ui/checkout-steps.

How does Checkout steps behave on phones?

On a phone each step is a full-height page: the step count and title on top, the total, Back and Continue at the bottom above the home indicator, hidden while the keyboard is up.

See also

Checkout PageForm WizardCart drawerCoupon inputRadio GroupPhone Input

Used in

Checkout Page

More React commerce components

Listing gridPricing tableReviewsSeller cardQuantity stepperPrice
All 16 React commerce components

Articles using this component

  • React Checkout Page: Cart, Steps, Coupons and a Pay Bar

    Build it: screens and apps · Oct 16, 2026 · 12 min read

  • React Stepper Component: States, Variants and Phones

    Component guides · Oct 13, 2026 · 8 min read

PreviousCategory pickerNext Coupon input

Checkout

Carpathia Market · Store

Secure payment · Free 30-day returns

  1. 1Delivery details
  2. 2Shipping
  3. 3Payment
  4. 4Review
Step 1 of 4Delivery details

Delivery details

Contact
Delivery address
Invoice
Totallei 1,572.70012345678901234567890,012345678901234567890012345678901234567890012345678901234567890.012345678901234567890012345678901234567890

Order summary

5 items
  • 1Pegas Popular city bike, 26"Quantity 1. Frame M · Mint greenlei 1,199, Was lei 1,399
  • 2iPhone 14 case, silicone, MidnightQuantity 2. Midnight · MagSafelei 159.80
  • 1Aluminum laptop standQuantity 1. Silver · adjustablelei 149
  • 1Carpathian Mountains guide (book)Quantity 1. 2026 edition · paperbacklei 64.90
    Subtotal
    lei 1,772.70
    Savings
    -lei 200.00
    Delivery
    Chosen in the next step
    Totallei 1,572.70012345678901234567890,012345678901234567890012345678901234567890012345678901234567890.012345678901234567890012345678901234567890