Skip to content
shadcn.io

shadcn.io is not affiliated with official shadcn/ui

Tag Picker Command Menu Block for Radix UI

A notes app's command menu hung under the tags field of a note: toggle tags on and off, or create a new one with a dot, ring, square, diamond, bar or triangle mark and apply it with Enter.

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

Tag a note and invent a tag in the same breath with this tag picker command menu for React with Radix UI. It opens from a note in Labelhill, a notes app, whose header holds an outline field with its ⌘ K cap, and the note is drawn under a soft scrim inside a hairline frame with a plus mark on each corner, with the menu hung under its tags field. The menu is the catalog Command, narrow: the tags the note has under Applied and the rest under Other tags, each row a neutral mark, the name and how many notes carry it. Enter applies or removes the highlighted tag on the note. Type a name that is not a tag yet and a first row "Create" appears; above the field a ToggleGroup of six marks (dot, ring, square, diamond, bar, triangle) picks the mark for the new tag, and Enter creates the tag and applies it. The marks are shapes, not colours, so tags tell apart in any theme. For preview the menu is drawn open in place when the page loads; the field, or Ctrl or ⌘ K while focus is in the block, opens the real command dialog with the same tags, mark and query. For notes, tickets, files and any item that carries a few labels.

FAQ

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

So you can see it without a click. On load the menu is the Command drawn inline over a picture of a note, in a plain shell that is not modal, so nothing is trapped, hidden or focused and the page around the preview keeps working. The header's field opens the real command dialog, modal, with its focus trap, Escape and focus return, and the inline copy stays closed until a reload. Both show the same Palette and share the block's state, so the tags in the copy is in the dialog. In your app keep the dialog and drop the copy: the "in-place" state, the scrim with its shell and the Escape and blur effects.

How does creating a tag work?

cleanName turns what you typed into a tag id: no leading hash, lower case, single hyphens for spaces and at most 24 characters. If no tag has that id, a Create row comes first and Enter adds the tag with the mark chosen in the switch, applies it to the note and clears the field. Typing a name that exists shows that tag instead, so there are never two with one name.

Why shapes instead of colours?

A colour needs a legend and fails for some eyes, while a shape carries its own meaning and survives a theme change. The six marks are drawn with borders, clip paths and the text colour, so they invert inside a chosen switch item, and each switch button is named (Dot, Ring, Square, Diamond, Bar, Triangle). The status line says which mark a new tag will get when you change it.

Why do rows move between Applied and Other tags?

Because applying is the point: the list shows what the note has first, so a tag you just applied jumps up and one you removed drops down, and the count in the foot ("2 applied of 8 tags") follows. The counts beside each tag add the note when it is applied. Choosing a row never closes the menu, so you can tag several in a row.

What stays local, and how do I wire it?

All of it. The eight starting tags, their marks and counts are typed constants, and a created tag lives only in the page. In your app fill START from your tags API, create the tag on the server inside pick (and wait for it before you mark it applied), and store each tag's mark with it so it looks the same everywhere.

Is it accessible from the keyboard?

Yes. Type to search, the arrow keys move through the rows, Enter applies, removes or creates, and Tab reaches the mark switch above the field. Escape closes the menu with focus on the field, and Ctrl or ⌘ K opens the dialog while focus is inside the block, and only then. The rows are options of a cmdk listbox; the block turns off its own fuzzy filter and lists the rows itself.