Components

Item

A flexible list-row primitive that composes media, title, description, and action slots into a single aligned row — the building block for settings lists, file rows, and feeds.

Basic usage

Compose ItemMedia, ItemContent, and ItemActions inside an Item.

Quarterly report

Variants

Three surface treatments — default for in-list rows, outline for standalone cards, and muted for inactive state.

Default
Outline
Muted

Media slots

ItemMedia renders a leading affordance: icon boxes a Lucide glyph, while image clips an avatar or thumbnail.

Icon media
Image media

Grouped lists

Wrap rows in an ItemGroup (a role="list" container) and divide them with ItemSeparator. Use asChild to make a whole row a link.

Header and footer bands

Item is a flex-wrap row, and ItemHeader and ItemFooter are basis-full — they take a full line of their own above and below the media/content/actions line, with their children pushed to opposite ends. Use them for metadata that belongs to the row but does not belong beside the title.

Invoice #1042Pending
March retainer
Sent 2 days agoNet 30

Props

Item — the row container.

PropTypeDefaultDescription
variant'default' | 'outline' | 'muted''default'Surface treatment of the row — transparent, bordered, or soft-filled with bg-muted/50.
size'default' | 'sm''default'default is p-4 gap-4; sm tightens the gap to 2.5 and takes its vertical padding from the --list-item-py token, so it follows the density axis.
asChildbooleanfalseMerge props onto the child element instead of rendering a div — use to make the whole row a link or button.
classNamestringAdditional Tailwind classes to merge.
...restReact.ComponentProps<'div'>All native div props are forwarded.

ItemMedia — the leading affordance.

PropTypeDefaultDescription
variant'default' | 'icon' | 'image''default'default is unstyled; icon renders a 32px boxed slot; image renders a 40px clipped, rounded slot for an <img>.
classNamestringAdditional Tailwind classes to merge.
...restReact.ComponentProps<'div'>All native div props are forwarded.

ItemSeparator — a preset Separator, so it takes the same props.

PropTypeDefaultDescription
orientation'horizontal' | 'vertical''horizontal'Forwarded to Separator. ItemSeparator presets it to horizontal because it divides stacked rows; pass it explicitly only if you really need a vertical rule.
decorativebooleantrueForwarded to Separator — leave it true so the rule is skipped by screen readers and the list role stays clean.
classNamestringMerged after the built-in my-0, so a margin override wins.

The remaining slots (ItemGroup, ItemContent, ItemTitle, ItemActions, ItemHeader, ItemFooter) forward native <div> props only; ItemDescription renders a <p>. The itemVariants and itemMediaVariants cva helpers are exported for building a row that has to match without being an Item.

Accessibility

  • ItemGroup applies role="list", but Item does not add role="listitem" — a list whose children are not list items is announced as empty, so supply the role yourself. On a plain row put it on the Item; on an asChild row the prop would land on the <a> and replace its link role, so wrap that row in <div role="listitem"> instead.
  • Use asChild with an <a> or <button> so the interactive row is a real, focusable element with native keyboard support.
  • Interactive rows receive a focus-visible ring using the brand gold ring token, visible on keyboard navigation and hidden on click.
  • Decorative media (icons, avatars) should be hidden from screen readers — give <img> an empty alt="" and let ItemTitle carry the accessible name.
  • ItemTitle is a <div>, not a heading — a long list of rows should not add dozens of entries to the document outline. If a row genuinely is a section heading, render one inside it.
  • ItemDescription is clamped to two lines, so never put information there that has no other home. Links inside it are underlined, not colour-only.