Patterns
List
View in FigmaNavigation and filter lists from flat item data — a `variant` enum picks link rows or filter tags; interactive state stays with the parent.
Install
npx shadcn@latest add @alix/listPreview
Usage
import { List, type ListProps } from "@/components/list";
const fromCMS: ListProps = await getList("capability-nav");
<List {...fromCMS} />
<List
variant="tags"
label="Capability filters"
items={[
{ text: "Restructuring", value: "restructuring" },
{ text: "Transactions", value: "transactions" },
]}
selected={selected}
onSelect={toggleFilter}
/>Examples
01
Compact navigation with a current page
The small size tightens the rows for secondary navigation; current marks the active page with aria-current.
<List
variant="list"
title="Capabilities"
size="small"
items={[
{ text: "Turnaround and restructuring", href: "/services/turnaround", current: true },
{ text: "Transactions and investor services", href: "/services/transactions" },
{ text: "Performance improvement", href: "/services/performance" },
]}
/>02
Controlled filter tags
The tags variant emits each stable value; the parent owns selection state and can read it back for a count.
Filter by capability1 active
const [selected, setSelected] = useState(["restructuring"]);
const toggle = (value: string) =>
setSelected((current) =>
current.includes(value)
? current.filter((item) => item !== value)
: [...current, value],
);
return (
<div className="w-full max-w-sm space-y-3">
<div className="flex items-center justify-between gap-4">
<span className="text-sm font-medium text-foreground">Filter by capability</span>
<Badge variant="primary">{selected.length} active</Badge>
</div>
<List
variant="tags"
label="Filter by capability"
items={[
{ text: "Restructuring", value: "restructuring" },
{ text: "Transactions", value: "transactions" },
{ text: "Performance", value: "performance" },
{ text: "Investigations", value: "investigations" },
]}
selected={selected}
onSelect={toggle}
/>
</div>
);03
Hover-linked to a preview panel
The onHover hook reports the focused row's index (or null on exit) so a sibling panel can preview it — the seam ServicesList uses.
Capabilities
Hover a capability to preview it.
const [active, setActive] = useState<number | null>(null);
return (
<div className="grid w-full max-w-2xl gap-8 md:grid-cols-2">
<List
variant="list"
title="Capabilities"
items={capabilities.map(({ text, href }) => ({ text, href }))}
onHover={setActive}
/>
<div className="flex items-center border border-border p-6 text-sm text-muted-foreground">
{active === null ? "Hover a capability to preview it." : capabilities[active].blurb}
</div>
</div>
);When to use it
Use it for
- Sidebar or footer navigation from a flat CMS link list, marking the current page.
- Filter chips whose selected state is lifted to the common ancestor of the filters and the results.
- Compact secondary navigation with size="small".
Reach for something else
- A single link or a styled call-to-action — use SectionLink instead.
- One filter chip on its own, or a bespoke tag layout — use ListItem directly instead.
- A full industries and capabilities directory with hover previews — use ServicesList instead.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| variant | "list" | "tags" | — | Required discriminant. `list` renders link rows; `tags` renders controlled filter chips. |
| items (list) | { text: ReactNode; href: string; current?: boolean }[] | — | Serializable link data. `current` marks the active page with `aria-current`. |
| items (tags) | { text: ReactNode; value: string }[] | — | Serializable filter options. `value` is the stable key emitted to the parent. |
| title / label | ReactNode / string | — | `title` is the visible heading and accessible name; use `label` when the list has no visible title. |
| size | "default" | "small" | "default" | List-row size. Applies only to `variant="list"`. |
| selected / onSelect | string[] / (value: string) => void | — | Controlled tag state. This is parent-owned behavior, not CMS content. |
| onHover | (index: number | null) => void | — | Optional parent hook for hover/focus previews, used by ServicesList. |