Navigation
Pinned bar
The bookmarks bar of the app — a row of pinned tasks, projects, docs and views for one-click return to whatever you keep coming back to. Pin from a searchable picker, drag to reorder, unpin on hover.
npx shadcn@latest add @orbit/pinned-barOverview
Hit + to search and pin an item, drag a pin to reorder (the drop indicator shows where it lands), or hover a pin and click × to unpin. Each pin carries its type’s icon and colour.
Click a pin to “open” it — a real app would navigate.
Empty state
An unpopulated bar invites curation instead of auto-filling — pins are only valuable because the user chose them.
Controlled
Pass pinnedIds + onPinnedChange to own the pin list — every pin, unpin and reorder reports the full ordered id list, ready to persist per user.
Order on the outside: inbox · ds
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| items | PinnedItem[] | — | Catalog of everything pinnable: { id, label, kind?, icon?, color? }. Picker candidates are the unpinned remainder. |
| pinnedIds / defaultPinnedIds | string[] | — | Controlled / uncontrolled pinned ids, in bar order. |
| onPinnedChange | (ids: string[]) => void | — | Fires on pin, unpin and reorder with the full ordered list. |
| onOpenItem | (item) => void | — | A pin chip was clicked — navigate to the record. |
| emptyText | string | "Pin a task, project or doc…" | Copy shown while nothing is pinned. |
| searchPlaceholder | string | "Search to pin…" | Picker input placeholder. |
| …rest | HTMLAttributes | — | Forwarded to the root <div>, including ref. |
Usage
Let the user curate it
Pins are personal. Don’t auto-populate the bar — its value is that everything on it was chosen deliberately.
Duplicate the sidebar
The Sidebar is the app’s fixed structure. The pinned bar is for the user’s own frequently-used items, not the same destinations.
Keep it to a handful
A pinned bar earns its place by being short. Overflow to a scroll or a “more” menu rather than wrapping endlessly.
Make unpinning scary
Pinning is low-stakes; so is removing. No confirm dialog — a click off and a click back on is the whole interaction.
Keyboard & accessibility
Pins and their unpin buttons are real buttons (the × becomes visible on focus, not just hover). The picker is a combobox: the input keeps focus while arrow keys move the highlighted option, announced via aria-activedescendant.