List
Rows of people, messages or settings: a headline with an overline and supporting text, an avatar or icon at the start, a tag at the end. The swipeable row reveals actions when swiped.
Preview
Mirrored is the same English content laid out right to left: it separates a mirroring bug from a translation one.
Installation
npx shadcn@latest add @ritla/listDependencies it brings: @phosphor-icons/react
Usage
import { List, ListItem } from "@/components/ui/list";<List aria-label="Team">
<ListItem leading={<Avatar name="Omar Haddad" size="sm" decorative />} supportingText="Product designer">
Omar Haddad
</ListItem>
</List>Examples
Icon and overline
leadingIcon puts the icon in a 40px circle. These rows are buttons (onClick), so they get the hover fill.
Swipeable row
Swipe toward the end to archive, toward the start to snooze or delete. Start and end follow the reading direction. The ⋮ button lists the same actions for keyboards, screen readers and mice.
Links and a static row
href or render={<Link />} makes a row a link. A row with neither a link nor onClick is static, with no hover fill.
API reference
List
| Prop | Type | Default | Description |
|---|---|---|---|
aria-label | string | The list's name when the page has several. | |
children | ReactNode | ListItem or SwipeableListItem rows. |
ListItem
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | Figma's Headline. | |
overline | ReactNode | Figma's Overline: a short line above the headline. | |
supportingText | ReactNode | Figma's Supporting text, below the headline. | |
leading | ReactNode | The start element: an avatar, a flag or an image. | |
leadingIcon | IconComponent | An icon in a 40px circle (Type = Icon). | |
trailing | ReactNode | Figma's Show Tags: a tag or any node at the end. | |
href | string | Makes the row a link. | |
render | ReactElement | A router link (<Link>) with the row's look. | |
onClick | MouseEventHandler | Makes the row a button. |
SwipeableListItem
| Prop | Type | Default | Description |
|---|---|---|---|
leadingActions | SwipeAction[] | Revealed at the start by swiping toward the end (Swipe = Leading). | |
trailingActions | SwipeAction[] | Revealed at the end by swiping toward the start (Swipe = Trailing). | |
SwipeAction | { label, icon, color?: "brand" | "warning" | "danger", onAction } | One 88px action. | |
menu | boolean | true | A ⋮ button with the same actions, so swiping isn't the only way. |
menuLabel | string | "More actions" | The ⋮ button's name; in the page language by default. |
… | ListItem props | Every ListItem prop. |
Accessibility
Keyboard
Keys are written for left-to-right. In right-to-left the arrow keys swap: the one pointing toward the reading direction moves forward.
| Key | Action |
|---|---|
| Tab | To the interactive rows and each swipeable row's ⋮ button. |
| Enter | Follows the link, presses the row or opens the actions menu. |
ARIA
- The list is a
uland each row anli, so screen readers announce the count. - An avatar next to the name is
decorative, so the name isn't read twice. - Swiping is never the only way: the ⋮ button lists the same actions. Without it (
menu={false}) the action buttons are focusable and the row opens when one gets focus. - An open row closes on a tap or focus outside it.
Arabic and RTL notes
Mirrors
- The avatar or icon sits on the right; the tag and ⋮ button on the left.
- Swiping follows the reading direction: in Arabic a swipe to the left reveals the start actions (on the right), a swipe to the right the end actions.
Does not mirror
- The action icons (archive, clock, trash) and avatars.
- Numerals
- Numbers in the text are as you pass them; format them with
formatNumber. - Arabic typography
- Headline
body-16Medium, supporting textbody-12, overlinelabel-12. Rows are at least 70px, so taller Arabic text grows them instead of clipping. - Mixed-direction text
- Wrap names in another language inside Arabic text in
<bdi>.