List
The canonical vertical list group: stacked rows with leading media, a title and description, and trailing meta or actions. Rows are inert by default and become links or buttons via asChild.
Installation#
Usage#
import {
List,
ListItem,
ListItemMedia,
ListItemContent,
ListItemTitle,
ListItemDescription,
ListItemMeta,
} from "@/components/ui/list"
export function Example() {
return (
<List>
<ListItem>
<ListItemContent>
<ListItemTitle>Inbox</ListItemTitle>
<ListItemDescription>12 unread</ListItemDescription>
</ListItemContent>
</ListItem>
</List>
)
}Playground#
Both shapes at once. Props belong to the list, except interactive, which is set per row on ListItem; Parts are the row slots you compose in or leave out.
- NotificationsPush, email and in-app
- StarredPinned to the top of every view
- ArchiveHidden from the inbox
A bordered surface, or rows flush on the page.
A hairline rule between rows.
Per row. Hover, press and focus affordance.
Leading icon, Avatar or thumbnail.
A second line under the title.
Trailing badge, timestamp or chevron.
<List className="w-full max-w-sm">
<ListItem>
<ListItemMedia>
<Bell weight="bold" />
</ListItemMedia>
<ListItemContent>
<ListItemTitle>Notifications</ListItemTitle>
<ListItemDescription>Push, email and in-app</ListItemDescription>
</ListItemContent>
</ListItem>
<ListItem>
<ListItemMedia>
<Star weight="bold" />
</ListItemMedia>
<ListItemContent>
<ListItemTitle>Starred</ListItemTitle>
<ListItemDescription>Pinned to the top of every view</ListItemDescription>
</ListItemContent>
</ListItem>
<ListItem>
<ListItemMedia>
<Archive weight="bold" />
</ListItemMedia>
<ListItemContent>
<ListItemTitle>Archive</ListItemTitle>
<ListItemDescription>Hidden from the inbox</ListItemDescription>
</ListItemContent>
</ListItem>
</List>Variants#
card (default) bands the rows inside a bordered surface; plain drops the chrome so rows sit flush on whatever holds them. Both keep the divider rule.
Interactive rows#
Pass asChild with an <a> or <button> to make the whole row a link/button: valid <li><a> nesting, with a hover fill that deepens on press, and an inset focus ring. It implies interactive; set interactive on its own for a row that handles onClick without being a link.
With controls#
Rows compose any Koala part in the meta slot. Here each row stays inert (it isn't a link) and drops a Switch on the right. The classic settings list.
Current row#
In a list-detail screen one row is the record the rest of the screen is showing: the open thread, the chapter being read. Mark it with current on ListItem. It answers on two channels a hover never touches, so the two can't be confused in any theme: the fill goes to full strength and stays there under the pointer, and a brand bar sits on the row's leading edge, the same mark the Sidebar's active row carries. It also sets aria-current on the row surface (the <button> or <a> under asChild); pass current="page" when the rows are navigation links.
Lines#
lines sets how many lines the title and the description may take. The title defaults to 1, one line with an ellipsis, so a long name never pushes the meta off the row; the description defaults to "none" and wraps in full. Pass 1 to keep an excerpt on one line, 2 or 3 to clamp at a word, or "none" on a title that is the content itself, like a pinned note. A truncate class on a description works too: its pretty wrapping is only ever a default.
API reference#
List
The root <ul>. Forwards all native <ul> props.
variant:"card"(default) |"plain".divided:boolean; hairline rule between rows (defaulttrue).asChild:boolean; render via Radix Slot.
ListItem
One row (<li>). Forwards native <li> props.
asChild: render the row surface as the single child (<a>/<button>) while keeping the<li>wrapper. Impliesinteractive.interactive:boolean; hover/press/focus affordance (defaults totruewhenasChildis set). On aplainlist the fill is a square band exactly as wide as the rules around it; withdivided={false}it rounds into a pill.current:boolean | "page" | "step" | "location" | "date" | "time"; the row the rest of the screen shows. Full fill, brand bar, andaria-currenton the row surface.
ListItemTitle, ListItemDescription
lines:1 | 2 | 3 | "none"; how many lines the text may take. Title defaults to1, description to"none".
ListItemMedia, ListItemContent, ListItemTitle, ListItemDescription, ListItemMeta
The row parts: leading media (icon/Avatar), the content column (title + description), and the trailing meta slot (badges, timestamps, a chevron, or an action). Each forwards native <div> props.