Skip to content

List ​

Displays a collection of related items, capable of carrying a title, description, and extra actions.

Basic Usage ​

Bordered list

Iteration v1.0 requirements listAll component unit tests passDocs site deployedReleased

Without Border ​

Default dividers

Only item dividers remainNo outer border

Size ​

size offers three density tiers: sm (compact) / md (default) / lg (spacious), adjusting row padding and title font size together.

Size

size="sm": compact listGreat for settings and briefssize="md": regular listNo size attribute means defaultsize="lg": spacious listGreat for rich media rows

Content Forms ​

Multiple content forms

NEW When no description slot is provided, content falls back to the default slot. Awaiting owner confirmationPending

Meta Structure (Avatar and Description) ​

oas-list-item provides structured Meta channels:

  • description attribute: quick text channel (mutually exclusive with slot="description", slot wins and supports rich content);
  • avatar attribute: avatar URL quick channel, rendered as a circular avatar at the row start;
  • slot="avatar": rich avatar channel (accepts oas-avatar or any custom content), takes precedence over the avatar attribute.

Avatar + description (attribute channel)

Online30 min agoTo confirm

Custom avatar (slot channel with oas-avatar)

ZJoinedWInvited

Leading Media (icon / image) ​

Besides the round avatar, rows have two more leading media slots; all three show/hide independently and can be combined as needed:

  • slot="icon": generic icon slot (e.g. oas-icon), size follows the content;
  • slot="image": square thumbnail slot, sized in step with the avatar scale (sm 24 / md 32 / lg 40), small rounded clip-fill;
  • slot="avatar": round avatar slot (see the previous section).

Icon slot and square thumbnail slot

12 docsCover thumbnailSyncedSpec thumbnail

href turns the row into a whole-row link: an <a> stretched over the row is rendered inside (accessible name taken from the row title) — clicking anywhere on the row navigates, keyboard Tab focuses and Enter activates; target controls how it opens (once set, rel="noopener noreferrer" is added automatically; an explicit rel is never overridden).

Link rows

Docs

Convention: a link row's click means navigation and no longer emits oas-click (kept separate from clickable rows to avoid double-trigger ambiguity); place in-row action controls in slot="extra" (the extra zone sits above the link overlay and stays interactive).

Row Interaction (clickable / selected) ​

With clickable on oas-list-item, the whole row becomes clickable: hover feedback, focusable (Enter / Space triggers), and an oas-click event on click. When interactive controls such as buttons or switches are embedded in a row, clicking them does not trigger the row click (row and control never fire together), and the row itself carries no interactive role so those controls are not wrapped in an interactive element. When row-level semantics are needed (e.g. role="button"), the host can set an explicit role (the component never overrides an explicit host role, consistent with card). selected marks the row with a highlight (visual state only). Selection is host-controlled — common in member lists and settings lists.

Clickable rows and selection

Email, inbox, desktop notificationsVisibility, data authorizationTwo-factor auth, device management

Click a row to toggle selection (single-select demo); clicking a switch does not change row selection.

slot="header" / slot="footer" render custom areas at the list edges — handy for group titles, statistics, and action buttons.

header / footer slots

Team members (3)+ Invite
Product managerFrontend engineerQA engineer
3 members · permission sync every Monday

Loading and Empty States ​

Loading state

Skeleton placeholder until loading finishesThe loading attribute handles the placeholder

Empty state

Setting empty forces the empty state; the list also shows it automatically when it has no oas-list-item children, and the copy can be customized via empty-text.

slot="empty" fully replaces the built-in empty state (icon + text), aligning with the data-component empty slot convention.

Custom empty slot

No matching tasks foundAdjust filters

Empty Text and Dividers ​

empty-text customizes the empty state text (default "No data").

Custom empty text

split controls the item dividers: by default (without bordered) dividers are included; setting bordered turns the dividers off, and split can be used to re-enable them when needed.

split dividers

bordered does not draw item dividers by defaultOnly the outer borderbordered + split adds item dividersBorder and dividers coexist

Stripe ​

stripe fills visually even rows (2nd, 4th, …) with a subtle background — great for audit logs and long comparison tables.

Stripe

Image and Text ​

The default slot of an item can hold a thumbnail, combining it with the title and description to form a rich-media list.

Image-text list (thumbnail + title + description)

WeeklyLogReleased

Data Channel (data + template dual channel) ​

Beyond declarative oas-list-item children, oas-list supports data-driven rendering: pass an array via the data property (recommended) or the data attribute (JSON string). Object rows work out of the box: without a template, the { title, description, avatar } fields are rendered as the Meta structure automatically (primitive values render as plain text) — rows never degrade to "[object Object]". There are two channels for customizing rendering (aligned with the oas-tree convention):

  1. template[slot="item"]: a static skeleton cloned per item, bound to data via the oas-item-render event (detail carries { index, item, element });
  2. Listen to oas-item-render only, without a template, and fill each row imperatively.

Attach listeners before assigning data: assignment renders synchronously and fires oas-item-render, so listeners attached afterwards miss the first render.

When data is present the data channel wins; without it the list falls back to declarative children. Data rows are carried by oas-list-item (same structure, avatar, selection, and clicks as declarative rows) with a data-index context on each row.

Data channel + oas-item-render

The data source has 5 task records. Click a row for the oas-click feedback.

template[slot="item"] skeleton clone

Scroll Loading (reach-bottom) ​

max-height turns the list body into a scroll container; scrolling to the bottom (threshold adjustable via bottom-offset, default 0) dispatches oas-reach-bottom, fired once per bottom entry and re-armed after scrolling away. Pair it with the slot="load-more" tail area for a "load more" button or a loading placeholder — the host controls fetching and when to stop.

Reach-bottom loading + load-more tail

Scroll to the bottom to append 5 rows (simulated async); after 3 batches it shows "No more"; bottom-offset="40" triggers 40px above the end.

Virtual Scrolling ​

Setting height enables virtual scrolling (embedding oas-virtual-list); row-height specifies the fixed row height (default 64 — data rows must be fixed-height). Virtual mode requires the data channel; massive datasets only render the visible window.

Virtual list with 10k rows

10,000 log records; only window rows + buffer are rendered.

Composition: Pagination (oas-pagination) ​

The list does not build in pagination — data is host-controlled, so pagination is just "host slicing + updating data". With oas-pagination it takes one slice:

Pagination composition

Composition: Card Wall (oas-grid + oas-card) ​

A grid card wall is composed from oas-grid + oas-card (the industry is also splitting grid out of list components: card styling is content, and the list only renders data):

Card wall composition

Composition: Grouped List ​

Data-channel items may carry a group field (consecutive items sharing a group form one section); the list automatically inserts a group header per section. The header text is the group value, or a custom label via the optional groupLabel field (falls back to group). In regular rendering the group header sticks to the top of the list's scroll container (pair with max-height so the body scrolls): the current group's header pins at the top while scrolling, and the next group header pushes it away when it reaches the top. Under virtual scrolling (height) group headers do not stick — grouped data falls back to full rendering and headers scroll as ordinary blocks; do not pair huge grouped data with height:

Grouped list (data channel · sticky headers)

Multiple contiguous groups in a height-limited scroll container — scroll down to watch headers stick and get pushed away by the next group header.

Group titles can also be composed with slot="header" sections or multiple oas-list blocks (declarative scenario, orthogonal to data-channel grouping):

Grouped list (header sections)

In progress
list / timeline batchIn progressData components auditIn progress
Done
Variant semantics unificationDone

Row hover feedback (hoverable) ​

hoverable gives non-clickable rows the same hover background feedback (off by default — today only clickable rows hover). Useful for scanning long read-only lists; selected rows keep their primary background. Works across all three row channels: data rows, declarative children, and virtual scroll rows.

hoverable on non-clickable rows

Hover any row: a light background covers the whole row (rows themselves are not clickable).

Sortable (sortable) ​

sortable enables row drag-and-drop reordering (HTML5 DnD): drop a row onto a target row and the list emits oas-reorder (detail: { from, to, item } — from / to are row indexes; item is the original data item for the data channel or the row element for declarative rows), letting the host reorder its data (the component never moves rows itself). Dropping onto the same row emits nothing; virtual-scroll rows are not supported. Works for both the data channel and declarative rows.

Sortable rows (sortable)

Drag any row onto another and drop.

API ​

oas-list ​

Attributes ​

AttributeDescriptionTypeDefault
borderedWhether to show the outer borderboolean—
bottom-offsetReach-bottom threshold (px): remaining distance to the bottom ≤ this counts as bottom, default 0string0
dataData channel (JSON string; the data / dataItems property takes precedence). With data the channel renders; without it falls back to declarative children. Item objects may carry group (group name — consecutive items sharing a group form one section with an auto-inserted header) and optional groupLabel (custom header text, falls back to group); headers stick in regular rendering (pair with a max-height scroll container) and do not stick under virtual scrollingunknown[]—
emptyForce empty state; auto empty when there are no childrenboolean—
empty-textEmpty state text——
heightVirtual scroll viewport height (px; setting it enables virtual mode, requires the data channel; data with group falls back to full rendering without sticky headers)string320
hoverableHover background for non-clickable rows (selected rows excluded)boolean—
loadingLoading state, shows skeleton placeholdersboolean—
max-heightMax height of the list body (px or CSS length); turns the body into a scroll container (pairs with oas-reach-bottom for scroll loading; group headers stick inside this container)string—
row-heightVirtual scroll row height (px, default 64; data rows must be fixed-height)string64
sizeRow density: sm / md (default) / lgstring—
sortableEnables row drag-and-drop reordering (data channel + declarative)boolean—
splitWhether to show item dividersboolean—
stripeZebra stripes: fills visually even rows with a subtle backgroundboolean—

Events ​

EventDescription
oas-clickItem click (data/virtual rows); detail carries
oas-item-renderDispatched after each data-channel row renders; detail carries
oas-reach-bottomScroll reached the bottom (fired once per bottom entry, re-armed after scrolling away); detail carries
oas-reorderFired on drop with the reordered row indexes and payload, detail: { from, to, item }

Slots ​

NameDescription
defaultDefault (declarative oas-list-item children)
emptyCustom empty state, fully replacing the built-in icon + text
footerFooter area (statistics, actions)
headerHeader area (group titles, actions)
load-moreTail "load more" area (button / loading placeholder)
template[slot="item"]Static skeleton cloned per data item, bound to data via oas-item-render

oas-list-item ​

Attributes ​

AttributeDescriptionTypeDefault
avatarAvatar URL quick channel (renders a circular avatar at the row start; slot="avatar" wins)string—
clickableWhole row clickable: hover feedback, focusable (Enter/Space triggers), click dispatches oas-click——
descriptionDescription text quick channel (slot="description" wins, rich content allowed)string—
hrefWhole-row link: an <a> stretched over the row is rendered inside (accessible name taken from the row title); clicking anywhere on the row navigates; link rows no longer emit oas-click (separated from the clickable semantics)string—
relLink rel (with href); when set explicitly the default is not auto-filledstring—
selectedSelected row highlight (visual state; selection is host-controlled)——
sizeRow density: sm / md (default) / lg——
targetLink open mode (with href); once set, rel="noopener noreferrer" is added automatically, an explicit rel is never overriddenstring—
titleItem title (rendered into the visible title region; absorbed from the host on read so no native hover tooltip remains; pass an empty string to clear); use slot="title" for rich contentstring—

Events ​

EventDescription
oas-clickRow click; detail carries { index, item } (data-row context injected by oas-list)

Slots ​

NameDescription
defaultDefault content: rendered in the description area (fallback for the description slot when not provided)
avatarRich avatar content (oas-avatar or anything), takes precedence over the avatar attribute
descriptionDescription area (falls back to the default slot when not provided)
extraExtra area on the right of the item
iconLeading icon slot (e.g. oas-icon), size follows the content; hidden when empty
imageSquare thumbnail slot (sized in step with the avatar scale sm 24 / md 32 / lg 40, small rounded clip-fill); hidden when empty
titleRich title content slot, overrides the title attribute text when present

Released under the MIT OR Apache-2.0 License.