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#

# One-time setup (tokens + lib helpers)npx koalaui-cli@latest init# Add this component (its dependencies come along)npx koalaui-cli@latest add list

Usage#

usage.tsxTSX
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.

  • Notifications
    Push, email and in-app
  • Starred
    Pinned to the top of every view
  • Archive
    Hidden from the inbox
Playground
Props

A bordered surface, or rows flush on the page.

A hairline rule between rows.

Per row. Hover, press and focus affordance.

Parts

Leading icon, Avatar or thumbnail.

A second line under the title.

Trailing badge, timestamp or chevron.

list.tsxTSX
<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.

  • Notifications
    3
  • Language
    English
  • Notifications
    3
  • Language
    English

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.

  • Push notifications
    Alerts on this device
  • Private profile
    Hide from search
  • Beta features
    Early access builds

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.

  • Freeze on the billing service from Thursday 18:00 until the migration lands.
    Ana Torres, pinned today
  • The on-call rotation moves to Mondays next month, so swap your shifts in the calendar before Friday and tell the team lead once you have.
    Marc Vidal, pinned yesterday, with a link to the rotation calendar and the swap form

API reference#

List

The root <ul>. Forwards all native <ul> props.

  • variant: "card" (default) | "plain".
  • divided: boolean; hairline rule between rows (default true).
  • 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. Implies interactive.
  • interactive: boolean; hover/press/focus affordance (defaults to true when asChild is set). On a plain list the fill is a square band exactly as wide as the rules around it; with divided={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, and aria-current on the row surface.

ListItemTitle, ListItemDescription

  • lines: 1 | 2 | 3 | "none"; how many lines the text may take. Title defaults to 1, 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.

FAQ#

Reach for List when you have rows of content with leading media, a title, and a description, like a settings or member list. Data Table is for columnar tabular data, and Description List is for key/value detail pairs.