List
Overview
A semantic container for displaying a vertical list of items. SList and SListItem are UI-only: they render the <ul>/<li> and the item/content/title/description anatomy themselves and share the listVariants style recipe (5 slots: root/item/content/title/description; 6 sizes) over a small UI-level context.
Use it for user lists, settings/menu groups, notification feeds, or any simple vertical collection. Prefer table for tabular data with columns and sorting, tree for hierarchical data, and select/combobox for selectable option lists.
There is no Aria list family: a plain ul/li carries no keyboard, focus or ARIA-widget logic of its own, so it has no place in the Aria layer. Interactive lists are served by the admitted listbox / tree families instead.
Usage
Features
- 🧩 UI-only anatomy shell —
SList/SListItemown the markup and sharelistVariantsclasses through a UI-levelprovideListUicontext; no Arialistfamily exists - 📋 Semantic markup — renders a real
<ul>/<li>withdata-vean-list-*hooks - 🏷️ Item composition —
SListItemrenders an optionaltitle+descriptionblock viatitle/descriptionprops or slots - ↔️ Leading / trailing —
leading/trailingslots onSListItemfor icons, badges, avatars or actions - 🎨 6 sizes — xs–2xl
sizematchingThemeSize - 🎛️ Per-slot control —
contentProps/titleProps/descriptionPropsforwarded to each item part - ♿ Accessible by default — semantic list semantics and zero
axe-coreviolations
Component family
SList— the list container; renders the<ul>with therootslot and provideslistVariantsto its itemsSListItem— the item row; renders the<li>plus theitem/content/title/descriptionnodes and exposesleading/trailing/title/description/default slots
Demos
Basic
Notifications
You have 3 unread messages.
This is ContentMessages
You have 5 new messages.
This is ContentSettings
Manage your account settings.
This is Content
API
Aria Layer
6List
Props
| Prop | Type | Default | Description |
|---|---|---|---|
class | ClassValue | — | Additional class names applied to the root element. |
size | ThemeSize | — | Visual size of the component. |
ui | Partial<ListUi> | — | Per-slot class overrides for the component. |
ListContent
ListDescription
ListItem
Props
| Prop | Type | Default | Description |
|---|---|---|---|
class | ClassValue | — | Additional class names applied to the item element. |
title | string | — | Title text rendered by the component. |
description | string | — | Description text rendered by the component. |
contentProps | ListContentProps | — | Properties forwarded to the content element. |
titleProps | ListTitleProps | — | Properties forwarded to the title element. |
descriptionProps | ListDescriptionProps | — | Properties forwarded to the description element. |
Slots
| Slot Name | Parameters | Description |
|---|---|---|
default | — | Custom content for the default slot. |
leading | — | Custom content for the leading slot. |
title | — | Custom content for the title slot. |
description | — | Custom content for the description slot. |
trailing | — | Custom content for the trailing slot. |
ListRoot
ListTitle
Notes
Architecture and benchmark differences
SList/SListItem are UI-only: a plain ul/li anatomy shell failed the Aria deletion test, so the family was removed from the Aria layer and the markup now lives in the UI layer, with all styling in listVariants and the slot classes passed down through provideListUi. This mirrors shadcn/ui’s composition-first approach, unlike Ant Design, Element Plus, Mantine and Naive UI which ship a config-driven list (dataSource/renderItem). VeanUI deliberately keeps SList a presentational container — data iteration stays with the consumer — so very large data is handled by the standalone virtualizer component rather than a built-in virtual scroll. Interactive lists use listbox / tree instead of SList.
| Capability | VeanUI | shadcn/ui | Ant Design List | Element Plus | Mantine List | Naive UI |
|---|---|---|---|---|---|---|
| Aria/styled split | ✅ | ✅ | — | — | — | — |
Semantic ul/li markup | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Title + description item | ✅ | — | ✅ | ✅ | ✅ | ✅ |
| Leading / trailing slots | ✅ | — | ✅ | ✅ | ✅ | ✅ |
| Size variants (6) | ✅ | — | ✅ | ✅ | ✅ | ✅ |
| Built-in virtual scroll | ➕ | — | ✅ | — | ✅ | ✅ |
— = unsupported or a different interaction model; ➕ = valuable enhancement (delegated to virtualizer).
Cautions
SListis a presentational container: it does not own data iteration or virtual scrolling. For 1k+ items render the items directly or compose them with the standalonevirtualizercomponent.- Items render as
<li>inside a<ul>. Keep the direct children list items and avoid nesting full interactive blocks that break list semantics. - Use
SListItemfor a title + description row; for a plain list you can also drop raw<li>elements intoSList. - The item
titlerenders as an<h3>and thedescriptionas a<p>; if the list is part of a document outline, picksize/classor your own markup so the heading level still fits.
Roadmap
A built-in virtualized list or a dataSource/renderItem config mode is evaluated as an enhancement, delegated to the standalone virtualizer today (see docs/roadmap/README.md).
FAQ
How do I build a list with title and description items?
Use SListItem with title/description:
<SList>
<SListItem title="Changelog" description="See what's new in v0.29" />
<SListItem title="Guides" description="Step-by-step tutorials" />
</SList>How do I add an icon or avatar to an item?
Use the leading slot (and trailing for actions):
<SListItem title="Jenny" description="Product designer">
<template #leading><SAvatar src="/jenny.png" fallback-label="J" /></template>
<template #trailing><SIcon icon="lucide:chevron-right" /></template>
</SListItem>How do I change the list size?
Pass size (xs–2xl):
<SList size="lg">
<SListItem title="Large list" />
</SList>How do I render a very large list efficiently?
SList is presentational — for 1k+ rows, render items directly or use the standalone virtualizer to keep only visible rows in the DOM.
How do I make an item clickable?
Wrap the clickable surface yourself (a link or SButton), since a list item is not interactive by default:
<SList>
<SListItem title="Open profile">
<template #trailing><a href="/profile" class="text-primary">View</a></template>
</SListItem>
</SList>