List
ProThe 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#
The preview is yours to explore. Its code comes with a Pro license, paid once and used in every project.
Have a license? Unlock it here
Anatomy#
import {
List,
ListItem,
ListItemMedia,
ListItemContent,
ListItemTitle,
ListItemDescription,
ListItemMeta,
} from "@/components/ui/list"
<List>
<ListItem>
<ListItemMedia />
<ListItemContent>
<ListItemTitle />
<ListItemDescription />
</ListItemContent>
<ListItemMeta />
</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.
The preview is yours to explore. Its code comes with a Pro license, paid once and used in every project.
Have a license? Unlock it here
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.
Bleed#
On a plain list an interactive row bleeds its fill 8px past the text column (-mx-2, a row 1rem wider, 2px between pills), so the text stays on the column and the fill gets air: the menu look, and the default. Inside a Card's padding, or where each row is a card of its own, that bleed lands on the frame. Passbleed= on the List: the row stays in the list's box, keeps its 8px inner padding, adds no gap of its own, and the spacing between rows is the container's (gap-* on the List).
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 an ink 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. When the selection moves with the keyboard, see Keyboard selection.
Keyboard selection#
The bar is right when the current row is one landmark in a list people point at. When the selection IS the cursor, moved by arrow keys or a game binding while focus stays on the container, a full fill and a 3px bar sit too close to a hover. PasscurrentStyle="ring" on the List: the current row takes the brand, a channel a hover never uses, as a faint brand wash and a 1px inset brand ring that follows the row's radius. Keep "bar" (the default) for pointer-driven lists and navigation; use "ring" wherever a key moves the selection.
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. Renders a <ul> and forwards all its props. Every part accepts className, merged last.
| Prop | Type | Default | Description |
|---|---|---|---|
| variant | "card" | "plain" | "card" | card bands the rows in a ringed surface; plain sits them flush on the ground. |
| divided | boolean | true | A hairline rule between rows. |
| bleed | boolean | true | plain only. Interactive rows bleed their fill 8px past the text column; false keeps them in the list's box, for rows in a Card or rows as cards. |
| currentStyle | "bar" | "ring" | "bar" | How the current row is marked. ring suits a keyboard-driven selection. |
| asChild | boolean | false | Renders the child (an <ol>, say) as the root via Radix Slot. |
ListItem
One row. Renders an <li> and forwards all its props.
| Prop | Type | Default | Description |
|---|---|---|---|
| asChild | boolean | false | Hands the row surface to a single <a> or <button> child while the <li> keeps the divider. Implies interactive. |
| interactive | boolean | - | Hover, press and focus affordance. Defaults to asChild. On a plain list the fill is a band as wide as the rules; with divided={false} it rounds into a pill. |
| current | boolean | "page" | "step" | "location" | "date" | "time" | - | The row the screen is showing: a full fill, an ink bar, and aria-current on the row surface. |
ListItemMedia
Leading media: a bare icon, an Avatar or a small image. Forwards native <div> props.
ListItemContent
The text column holding title and description. Forwards native <div> props.
ListItemTitle
The primary line. Forwards native <div> props.
| Prop | Type | Default | Description |
|---|---|---|---|
| lines | 1 | 2 | 3 | "none" | 1 | Lines before the ellipsis, so a long title never pushes the meta off the row. none wraps in full. |
ListItemDescription
The muted secondary line. Forwards native <div> props.
| Prop | Type | Default | Description |
|---|---|---|---|
| lines | 1 | 2 | 3 | "none" | "none" | Wraps in full by default; 1 truncates, 2 or 3 clamp. |
ListItemMeta
The trailing slot: badges, a timestamp, a chevron or a control. Forwards native <div> props.