ListItem — spec¶
Synthesized from MUI
ListItem. The single-row primitive used insideList. For interactive rows that respond to clicks, usecomponent-list-item-button.md—ListItemitself is the structural container.
When to use¶
- One row inside a
<List>. - Pure-display rows (text + secondary action button on the right).
- For clickable rows (the common case in settings / nav lists), use
ListItemButtonas the child.
Anatomy¶
┌──────────────────────────────────────────────────┐
│ [icon/avatar] Primary text [secondary] │
│ Secondary text │
└──────────────────────────────────────────────────┘
Slots: leading icon/avatar (optional), text block (ListItemText), trailing secondary action (ListItemSecondaryAction).
API¶
<List>
<ListItem secondaryAction={<IconButton><DeleteIcon /></IconButton>}>
<ListItemAvatar><Avatar /></ListItemAvatar>
<ListItemText primary="이름" secondary="설명" />
</ListItem>
</List>
| Prop | Type | Default | Description |
|---|---|---|---|
children |
ReactNode |
— | Primary content; usually ListItemText + optional avatar/icon |
disablePadding |
boolean |
false |
Remove default vertical padding (use when child is ListItemButton) |
secondaryAction |
ReactNode |
— | Right-anchored action area; not part of the row's clickable surface |
disableGutters |
boolean |
false |
Remove horizontal padding |
dense |
boolean |
false |
Reduce vertical padding (settings / dense nav lists) |
divider |
boolean |
false |
Bottom divider line |
States¶
ListItem itself is non-interactive. Interactivity comes from a ListItemButton child:
| State (on inner button) | Visual |
|---|---|
| Default | transparent bg, fg-default |
| Hover | bg-subtle |
| Focus-visible | 2px focus ring inset; cite keyboard-and-focus.md |
| Active | bg-pressed |
| Selected | bg-selected, fg-on-selected |
| Disabled | reduced opacity |
Tokens consumed¶
--color-bg-default
--color-fg-default
--space-sm /* dense */
--space-md /* default */
--list-row-min-height-44
--color-divider
Accessibility¶
- Semantic element:
<li>(default). - For navigable list with selection, use
role="list"on the parent andaria-current="page"on the selected item. secondaryActionmust be a separately-focusable control with its own accessible name (otherwise it merges into the row's tab stop and is unreachable).- Touch target: ≥ 44pt for mobile lists.
Edge cases¶
- Long primary text — wraps to 2 lines, then ellipsis.
ListItemButtonchild +secondaryAction— the secondary action is OUTSIDE the button's hit area. Functionally critical for screen-reader nav.- Korean text density — Hangul reads ~10% wider than Latin. Test with realistic Korean copy.
Code example¶
<List>
{users.map((u) => (
<ListItem
key={u.id}
secondaryAction={
<IconButton edge="end" aria-label={`${u.name} 삭제`}>
<DeleteIcon />
</IconButton>
}
disablePadding
>
<ListItemButton onClick={() => select(u.id)}>
<ListItemAvatar><Avatar src={u.avatarUrl} /></ListItemAvatar>
<ListItemText primary={u.name} secondary={u.role} />
</ListItemButton>
</ListItem>
))}
</List>
Don't¶
- Don't make the entire
ListItemclickable viaonClick; wrap inner content inListItemButtonso secondary action stays separately focusable. - Don't put 3+ controls inside
secondaryAction— overflow into a menu.
References¶
- MUI:
ListItem.d.ts