View switch
betaA row of views. The active one slides to the view you pick.
Preview
Showing board
Install
1. Add the registry to
components.json, once per app. Without it, the shadcn CLI looks up@txbrendanin a public list that txbrendan/ui does not control.npx shadcn@latest registry add "@txbrendan=https://txbrendan.dev/r/{name}.json"2. Add the theme, once per app. It replaces your color tokens with ones checked for contrast.
npx shadcn@latest add @txbrendan/theme3. Add the component.
npx shadcn@latest add @txbrendan/view-switch
Use it for
- Choosing one layout of the same records, like Board, List, and Timeline.
- Pass options when the views are not those three. Each option needs a value and a label.
- Hold the value yourself when another part, such as a view morph, must follow it.
Avoid it for
- A setting that is not a view of the same things. Use Tabs and put a panel under each one.
- More than a handful of views. The row scrolls, but a menu is easier to scan.
Props
- options
The views, in order.
{ value: string; label: string; id?: string; controls?: string }[] · default [{ value: "board", label: "Board" }, { value: "list", label: "List" }, { value: "timeline", label: "Timeline" }]
- value
The selected view. Use with onValueChange.
string
- defaultValue
The view selected on first paint, when value is not set. Defaults to the first option.
string
- onValueChange
Called with the view that was just chosen.
(value: string) => void
- tone
Color of the active pill or line.
"neutral" | "brand" · default "neutral"
- emphasis
"solid" puts the active view on a filled pill. "ghost" underlines it.
"solid" | "ghost" · default "solid"
- size
Height, padding, and text size.
"sm" | "md" | "lg" · default "md"
- aria-label
Names the group, like "Layout".
string · default "View"
- className
Classes on the root. Later classes win.
string
| Prop | Type | Default | What it does |
|---|---|---|---|
| options | { value: string; label: string; id?: string; controls?: string }[] | [{ value: "board", label: "Board" }, { value: "list", label: "List" }, { value: "timeline", label: "Timeline" }] | The views, in order. |
| value | string | - | The selected view. Use with onValueChange. |
| defaultValue | string | - | The view selected on first paint, when value is not set. Defaults to the first option. |
| onValueChange | (value: string) => void | - | Called with the view that was just chosen. |
| tone | "neutral" | "brand" | "neutral" | Color of the active pill or line. |
| emphasis | "solid" | "ghost" | "solid" | "solid" puts the active view on a filled pill. "ghost" underlines it. |
| size | "sm" | "md" | "lg" | "md" | Height, padding, and text size. |
| aria-label | string | "View" | Names the group, like "Layout". |
| className | string | - | Classes on the root. Later classes win. |
Accessibility
- The row is a tab list. Each view is a tab. Arrow keys move between them and select the one they reach.
- Home and End jump to the first and last view.
- The active view's pill or line slides. Reduced motion jumps there instead.
Examples
Board, list, and timeline
<ViewSwitch value={layout} onValueChange={setLayout} aria-label="Layout" />Your own views
<ViewSwitch
aria-label="Layout"
options={[{ value: "cards", label: "Cards" }, { value: "rows", label: "Rows" }]}
defaultValue="cards"
/>