Skip to content
shadcn.io

shadcn.io is not affiliated with official shadcn/ui

Edit Product Drawer CRUD Block for React

A shop back office's product list whose Edit button opens a bottom drawer for name, price, cost, stock and status, with the margin worked out as you type, Revert, Save changes and Undo last save.

Framework
UI library

Preview

Preview width

Installation

Part of Pro
Pro unlocks the code and one-line install of every Pro button and text effect, plus the MCP for your AI editor.
Have Pro already? Sign in to open it.Opens here as soon as you're Pro.

Overview

Let someone change a product and see what the change does to the margin, with this edit drawer for React with Base UI. It starts from the Products page of Listwick, a shop back office: five real rows with price, margin, stock and a status Badge, each with an outline Edit Button, under a soft scrim inside a hairline frame with a plus mark on each corner. The Drawer rises from the bottom and holds a name, a price and a cost, the margin worked out from them as you type ("Margin 38%", and a sign with "Low, under 20%" when it falls below that), a stock count and a Draft or Live Segment Group. The foot says how many fields changed beside Revert, which goes back to the values at open without closing, and Save changes, the only Shine Button, which waits until something changed. Saving updates the row behind the drawer, and the foot of the list says "Saved Linen cushion." beside "Undo last save". Closing a changed drawer swaps its foot for "Close without saving?" in the same space. For back offices, catalogues and any record whose fields depend on each other.

FAQ

Why is the drawer already open, and what do I keep?

So you can see it without a click. On load the drawer is a copy drawn in place, docked to the foot of the real product list: a native dialog element that isn't modal, so nothing is trapped, hidden or focused. A row's Edit opens the real Drawer, with its swipe, Escape and focus back on that row, and the copy stays closed until a reload. Both show the same ProductFields component and share the block's state. In your app keep the Drawer and drop the copy: the "in-place" state, the scrim and the dialog element in the frame.

How is the margin worked out?

It is the price minus the cost, as a whole percent of the price, from the two amounts you have typed: 24.00 and 14.80 give 38%. It follows every keystroke because the two money fields are plain inputs with their own parsing. Under 20% it adds a warning sign and "Low, under 20%"; a cost above the price says "Below cost"; an amount that is not a number says what is missing instead of showing a figure.

What do Revert and the close question do?

Revert puts every field back to the values at open and leaves the drawer open; with nothing changed it answers "No changes to revert." Closing with unsaved changes (the close button, Escape, the swipe or the scrim) turns the foot into "Close without saving?" with Keep editing, which takes focus, and Close anyway. With nothing changed it closes at once.

What does Undo last save restore?

The product as it was before the last save, in the list behind the drawer, and focus goes back to its Edit button. It undoes one save only; any new save replaces it, and nothing times out.

What stays local, and how do I wire a real save?

The five products are typed in PRODUCTS; amounts are kept in cents and the saved values replace the row in state. A reload brings the originals back and nothing is sent. In your app post the draft in save, show the server's refusal under the field it names, and update the row from the response. Undo here sets the old list back; against a server it should call a restore endpoint.

Is it accessible from the keyboard?

Yes. The real drawer is named by its title and described by its sentence, focus starts in the Name field, and Escape asks before it closes a changed form. Every field has a label and a problem slot that is always rendered, and the status group is named. The drawer differs a little between libraries (the swipe handle, where focus lands on opening), so the block moves focus to the name itself. The copy shown on load moves no focus; Escape inside it behaves like the real drawer's.