shadcn.io is not affiliated with official shadcn/ui
Tag Manager CRUD Block for React
A photo library's tags with counts worked out from the photos: a strip of tags and unused ones, rename in place, a Merge popover when a rename lands on an existing name, and a delete that says how many photos lose the tag.
Installation
Overview
Keep a tag list tidy where renaming, merging and deleting each show what they do to the records that carry the tag, with this tag manager for React with Base UI. A hairline frame with a plus mark on each corner opens on a strip of two numbers, how many tags and how many are unused, then a quick line that adds a lowercase, unique tag, then one frame of fixed height holding nine tags, each reading "Used by 4 photos" (counted from the twelve photos that carry them, so nothing is typed twice). A name is an Editable: click or press Enter to edit, Enter saves, Escape cancels. Rename it to a name that already exists and a Popover opens on that row, "Merge into garden?", with the sum worked out in a sentence (4 photos and 2, none carry both, so 6 end up with garden); Merge joins them, Keep both is refused because two tags can't share a name, and the row's own Merge button opens the same popover for any other tag. Delete is an Alert Dialog that says how many photos lose the tag, and Undo in the foot brings back the tag and its photos. Add is the only Shine Button, the bands are ruled by Guides, and deleting every tag shows an Empty state. For photo libraries, label systems, content categories and any record you can attach to many others.
FAQ
How do I fill it with my own tags and photos?
Edit TAGS (an id and a name) and PHOTOS (an id and the ids of the tags it carries). The counts, the unused count in the strip and every sentence in the merge and delete dialogs are derived from those two lists. New tag ids come from a counter held in state.
What does merging do to the photos?
The tag that goes is replaced by the one that stays on every photo that carried it, and a photo that carried both ends up with the one tag once, which is why the popover's sum subtracts the photos that carry both. The tag that stays keeps its name, the row of the tag that went disappears, and focus lands on the row that stayed. Undo puts both tags and every photo back as they were.
What is checked when I rename or add a tag?
Names are trimmed, lowercased, limited to 20 characters and may hold letters, digits, spaces and hyphens. An empty or malformed name is refused with its reason in a slot under the field that is always there, so the list never changes height. A name that belongs to another tag is not an error when renaming: it opens the merge popover. In the quick line it is refused, since adding a duplicate makes no sense.
Why does Delete ask first when Undo exists?
Because deleting changes photos, not just a row. The dialog names the number of photos that lose the tag, its first focus is Cancel, and its confirm is a destructive button. After the confirm the foot says "Deleted garden; removed from 4 photos." and Undo restores the tag and its photos together.
What stays local, and how do I wire a real save?
Tags and photos live in state in this tab and a reload brings the originals back. In your app call your API in add, rename, mergeNow and remove; a merge should be one server transaction that repoints the photos and deletes the tag, and the same sums can be shown from the server's counts before it runs.