List
Displays a collection of related items, capable of carrying a title, description, and extra actions.
Basic Usage
Bordered list
Without Border
Default dividers
Size
size offers three density tiers: sm (compact) / md (default) / lg (spacious), adjusting row padding and title font size together.
Size
Content Forms
Multiple content forms
Meta Structure (Avatar and Description)
oas-list-item provides structured Meta channels:
descriptionattribute: quick text channel (mutually exclusive withslot="description", slot wins and supports rich content);avatarattribute: avatar URL quick channel, rendered as a circular avatar at the row start;slot="avatar": rich avatar channel (acceptsoas-avataror any custom content), takes precedence over theavatarattribute.
Avatar + description (attribute channel)
Custom avatar (slot channel with oas-avatar)
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
Whole-row Link (href / target)
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
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
Click a row to toggle selection (single-select demo); clicking a switch does not change row selection.
Header and Footer
slot="header" / slot="footer" render custom areas at the list edges — handy for group titles, statistics, and action buttons.
header / footer slots
Loading and Empty States
Loading state
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
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
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)
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):
template[slot="item"]: a static skeleton cloned per item, bound to data via theoas-item-renderevent (detailcarries{ index, item, element });- Listen to
oas-item-renderonly, 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)
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
| Attribute | Description | Type | Default |
|---|---|---|---|
bordered | Whether to show the outer border | boolean | — |
bottom-offset | Reach-bottom threshold (px): remaining distance to the bottom ≤ this counts as bottom, default 0 | string | 0 |
data | Data 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 scrolling | unknown[] | — |
empty | Force empty state; auto empty when there are no children | boolean | — |
empty-text | Empty state text | — | — |
height | Virtual scroll viewport height (px; setting it enables virtual mode, requires the data channel; data with group falls back to full rendering without sticky headers) | string | 320 |
hoverable | Hover background for non-clickable rows (selected rows excluded) | boolean | — |
loading | Loading state, shows skeleton placeholders | boolean | — |
max-height | Max 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-height | Virtual scroll row height (px, default 64; data rows must be fixed-height) | string | 64 |
size | Row density: sm / md (default) / lg | string | — |
sortable | Enables row drag-and-drop reordering (data channel + declarative) | boolean | — |
split | Whether to show item dividers | boolean | — |
stripe | Zebra stripes: fills visually even rows with a subtle background | boolean | — |
Events
| Event | Description |
|---|---|
oas-click | Item click (data/virtual rows); detail carries |
oas-item-render | Dispatched after each data-channel row renders; detail carries |
oas-reach-bottom | Scroll reached the bottom (fired once per bottom entry, re-armed after scrolling away); detail carries |
oas-reorder | Fired on drop with the reordered row indexes and payload, detail: { from, to, item } |
Slots
| Name | Description |
|---|---|
| default | Default (declarative oas-list-item children) |
empty | Custom empty state, fully replacing the built-in icon + text |
footer | Footer area (statistics, actions) |
header | Header area (group titles, actions) |
load-more | Tail "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
| Attribute | Description | Type | Default |
|---|---|---|---|
avatar | Avatar URL quick channel (renders a circular avatar at the row start; slot="avatar" wins) | string | — |
clickable | Whole row clickable: hover feedback, focusable (Enter/Space triggers), click dispatches oas-click | — | — |
description | Description text quick channel (slot="description" wins, rich content allowed) | string | — |
href | 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; link rows no longer emit oas-click (separated from the clickable semantics) | string | — |
rel | Link rel (with href); when set explicitly the default is not auto-filled | string | — |
selected | Selected row highlight (visual state; selection is host-controlled) | — | — |
size | Row density: sm / md (default) / lg | — | — |
target | Link open mode (with href); once set, rel="noopener noreferrer" is added automatically, an explicit rel is never overridden | string | — |
title | Item 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 content | string | — |
Events
| Event | Description |
|---|---|
oas-click | Row click; detail carries { index, item } (data-row context injected by oas-list) |
Slots
| Name | Description |
|---|---|
| default | Default content: rendered in the description area (fallback for the description slot when not provided) |
avatar | Rich avatar content (oas-avatar or anything), takes precedence over the avatar attribute |
description | Description area (falls back to the default slot when not provided) |
extra | Extra area on the right of the item |
icon | Leading icon slot (e.g. oas-icon), size follows the content; hidden when empty |
image | Square thumbnail slot (sized in step with the avatar scale sm 24 / md 32 / lg 40, small rounded clip-fill); hidden when empty |
title | Rich title content slot, overrides the title attribute text when present |