KQ-1337 · RETURN/FLOW

Return Flow

A returns flow built from data that ends as paper in your hand: pick the items (each tick draws on flick), give each a reason as chips, rows or tiles, choose where the refund goes while its digits roll on snap, and the label feeds out of a printer slot in 3px steps like a stepper motor. Pull it down and a rip runs along the perforation from the left while the label pivots about its unripped corner; past the line it comes free and lands on recoil, and released early it swings back on snap with the throw's velocity. Paged, the steps move by direction inside a box that glides to each panel's measured height under a rail whose pill slides on snap; every pick, reason and card is a real checkbox or radiogroup, and Tear off does by key what the pull does by hand.

KQ-1337 · RETURN/FLOW

Start a return

Order FW-20418 · delivered Sep 24 · Fernworks

23 days left

What's going back?

Returns open until Oct 24. Pick what is going back.

Nothing picked yet

step 1 of 4 · items

Install

One command — the source lands in your repo. Or copy it from the Code tab.

$ pnpm dlx shadcn@latest add @kinetiq/return-flow

Props

print
"label" | "qr"
default "label"
What comes out of the slot: a shipping label with carrier band, addresses and a seeded barcode, or a printer-free slip with a large QR and a short counter code.
steps
"paged" | "inline"
default "paged"
Four pages (Items, Reason, Refund, Label) under a step rail with Back and Continue, or one page where reasons unfold under each picked item and the printer arrives at the end.
reasons
"chips" | "rows" | "tiles"
default "chips"
How a reason is picked: wrapping pills whose check slides in, rows with a detail line and a growing dot, or two- and three-up icon tiles. Each is a radiogroup.
order
ReturnOrder
default defaultReturnOrder
The order: id, merchant, delivered date (ms), return window in days, the payment a refund goes back to, both addresses and the items (name, variant, price, quantity, kind, tint, returnable).
reasonOptions
ReturnReason[]
default defaultReturnReasons
The reasons on offer. A reason with note opens a Tell us more field; one with waivesFee (a fault) drops the return fee.
methods
RefundMethod[]
default defaultRefundMethods
Where the money goes. bonus adds credit as a share of the items; fee: false waives the return fee.
dropoffs
ReturnDropoff[]
default defaultReturnDropoffs
How the parcel leaves; a fee is taken off the refund.
value / defaultValue
ReturnSelection
default nothing picked, the first method and drop-off
The selection: one line per picked item (quantity, reason, note) plus the refund method and drop-off. Controlled or uncontrolled.
onValueChange
(value: ReturnSelection) => void
default —
Fires from the pick, stepper, reason, note or card that changed the selection.
step / defaultStep
number
default 0
The page, 0 to 2 while editing; the label page (3) shows whenever the label state is ready.
onStepChange
(step: number) => void
default —
Fires from Continue, Back or a rail stop, and with 3 when the label is made.
onCreateLabel
(selection: ReturnSelection, refund: ReturnRefund) => void | Promise<ReturnShippingLabel | void>
default —
Make the label. A promise holds the button pending with its width kept; resolve with your label to print it, reject to show the error.
state
"idle" | "pending" | "ready" | "error"
default —
Controlled label state. Ready prints the label; only a print the visitor started makes sound.
onStateChange
(state: ReturnLabelState) => void
default —
Fires as the label is requested, made or refused.
shippingLabel
ReturnShippingLabel
default one made from the order, seeded
The label to print: carrier, tracking, RMA, counter code and drop-by date (ms).
onTear
() => void
default —
The label was torn off, by a pull or by Tear off.
onDownload
() => void
default —
Download on a torn label. Without it there is no Download button.
onEmail
() => void
default —
Email it on a torn label. Without it there is no Email button.
onStartOver
() => void
default —
Start over was pressed: the selection and the label are cleared and the flow returns to its first page.
returnFee
number
default 4.95
The return fee, taken off the refund unless the method or a fault reason waives it.
now
number | Date
default defaultReturnNow
The flow's moment: the days-left pill and the default drop-by date count from it, never the wall clock.
formatPrice
(amount: number) => string
default US dollars
Turns amounts into text everywhere they appear.
title
string
default "Start a return"
The heading, and the region's name unless label is given.
status
"ready" | "loading" | "error"
default "ready"
Whether the order has arrived: loading shows skeleton rows, error a message with Try again.
onRetry
() => void
default —
Try again was pressed after the order failed to load.
label
string
default —
The region's accessible name, when the visible title is not enough.
sound
boolean
default false
Clicks for picks, reasons and steps, soft paper bursts as a visitor-started label feeds and a louder paper when it tears.
disabled
boolean
default false
Read only: everything shows, nothing can be changed.
className
string
default —
Classes for the root. It is at most 560px tall and scrolls inside itself.