List

Pro

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# Activate your Pro license (once per machine)npx koalaui-cli@latest login <key># Add this component (its dependencies come along)npx koalaui-cli@latest add list

Usage#

This example is part of Koala UI Pro

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.

  • 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.

This example is part of Koala UI Pro

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.

  • 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.

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).

Units

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

  • Robbery in progress
    Vinewood Blvd
    2m
  • Person down
    Legion Square
    6m
  • Abandoned vehicle
    Route 68
    14m
  • Traffic light out
    Strawberry Ave
    21m

Focus the queue, then ↑ ↓

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. Renders a <ul> and forwards all its props. Every part accepts className, merged last.

PropTypeDefaultDescription
variant"card" | "plain""card"card bands the rows in a ringed surface; plain sits them flush on the ground.
dividedbooleantrueA hairline rule between rows.
bleedbooleantrueplain 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.
asChildbooleanfalseRenders the child (an <ol>, say) as the root via Radix Slot.

ListItem

One row. Renders an <li> and forwards all its props.

PropTypeDefaultDescription
asChildbooleanfalseHands the row surface to a single <a> or <button> child while the <li> keeps the divider. Implies interactive.
interactiveboolean-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.
currentboolean | "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.

PropTypeDefaultDescription
lines1 | 2 | 3 | "none"1Lines 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.

PropTypeDefaultDescription
lines1 | 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.

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.