Skip to content
shadcn.io

shadcn.io is not affiliated with official shadcn/ui

Add to Collection Dialog Block for Radix UI

A recipe page with its save dialog shown open: a search over six collections, a checkbox row for each with its recipe count, a New collection field that refuses a duplicate name, and a line that counts the collections holding the recipe.

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 people file one thing in several places at once, with this save dialog for React with Radix UI. It opens from the Lentil and fennel soup page on Larderfold, a recipe keeper: the header holds an outline Save Button that reads "Saved in 2" and fills its bookmark once the recipe is in a collection, and the page, a plate drawn in lines beside the title and an ingredient list, is drawn under a soft scrim inside a hairline frame with a plus mark on each corner. The Dialog holds a search Input over six collections, each a Checkbox row with the number of recipes in it, which goes up by one when this recipe is ticked into it. Under the list one line says "Saved to 2 collections". A New collection Field adds another at the top of the list, already ticked, and says so in place when the name already exists. Done, the only Shine Button, closes the dialog. Ticking works at once, so there is no Cancel. For preview the dialog is drawn open in place when the page loads; the header button opens the real Dialog, with the same list. For recipe, reading, playlist and photo apps, and anything that can sit in more than one collection.

FAQ

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

So you can see it without a click. On load the dialog is a copy drawn in place over the page: a native dialog element that isn't modal, so nothing is trapped, hidden or focused, and the page around the preview keeps working. The Save button opens the real Dialog, modal, with its focus trap, Escape and focus return, and the copy stays closed until a reload. Both show the same CollectionFields component and share the block's state, so a collection ticked in the copy is there in the real one. In your app keep the Dialog and drop the copy: the "in-place" state, the scrim and the dialog element inside the frame.

Why is there no Save or Cancel button?

Because every tick is the change. Membership is the state, and the Save button and the count under the list follow it as you tick, so Done only closes the dialog. If your server needs a request for each change, send it in toggle, keep the row's checkbox as it is while the request runs, and put a failure in the polite status message. If you want a real Cancel, keep a copy of the starting members and write it back when the dialog closes without Done.

How does New collection work?

Type a name and press Create, or press Enter in the field: Enter creates the collection and never submits the dialog. The name is trimmed and compared without regard to case against every collection, so "weeknight" is refused with a line under the field and focus stays there. A new one goes to the top of the list, ticked, with a count of zero plus this recipe, and the search clears so you can see it. Names stop at 32 characters.

What stays local in the demo, and how do I wire it up?

Everything. The collections, the ticks and the new names live in this page's state, no request is made, and the polite status message says "Done in this demo; nothing was stored." In your app load the collections and the ones that hold the recipe from your API, add and remove the recipe in toggle, create a collection in create and take its id from the response, and keep the counts honest by reading them back instead of adding one in the browser.

Is it accessible from the keyboard?

Yes. The real dialog is named by its title and described by its sentence, focus starts in the search field and stays inside until it closes, and Escape, Cancel and the close button all return focus to Save. The trigger is the Save button wrapped in DialogTrigger with asChild, and the dialog is controlled with open and onOpenChange. The copy shown on load moves no focus; Escape inside it, Cancel and its close button close it and put focus on Save.

How does it look on a phone?

The copy shown on load docks to the foot of the page as a bottom sheet with Done pinned at the bottom and the search, the list and the New collection field scrolling above it. The real dialog keeps the Dialog's own phone layout: a sheet stuck to the bottom of the screen.