Skip to content

Item

A flexible list row with a leading media slot (icon box, avatar or picture), a title, a description and trailing actions. The whole row can be a link or a button while its actions stay separately clickable.

Displayv0.1.0experimentalWCAG 2.2 AAView spec
  • Ada Lovelace
    Engineering lead
  • Grace Hopper
    Platform
  • Katherine Johnson
    Data
import { Avatar, Item, ItemContent, ItemDescription, ItemGroup, ItemMedia, ItemTitle } from "@rdloom/react";

const people = [
  { name: "Ada Lovelace", role: "Engineering lead" },
  { name: "Grace Hopper", role: "Platform" },
  { name: "Katherine Johnson", role: "Data" },
];

export default function ItemBasicExample() {
  return (
    <div className="flex w-full justify-center">
      <ItemGroup aria-label="Team" className="max-w-sm">
        {people.map((p) => (
          <Item key={p.name}>
            <ItemMedia>
              <Avatar name={p.name} decorative />
            </ItemMedia>
            <ItemContent>
              <ItemTitle>{p.name}</ItemTitle>
              <ItemDescription>{p.role}</ItemDescription>
            </ItemContent>
          </Item>
        ))}
      </ItemGroup>
    </div>
  );
}

Installation

npx rdloom add item

Copies the source into src/components/rdloom. Edit it freely: rdloom upgrade merges later versions into your changes. It needs react-aria-components; add --install to install them.

Use another registry client
npx shadcn@latest add https://rdloom.vimalbhatt.com/r/item.json

Works, but without upgrade tracking.

Usage

import { Avatar, Item, ItemContent, ItemDescription, ItemGroup, ItemMedia, ItemTitle } from "@rdloom/react";

<div className="flex w-full justify-center">
  <ItemGroup aria-label="Team" className="max-w-sm">
    {people.map((p) => (
      <Item key={p.name}>
        <ItemMedia>
          <Avatar name={p.name} decorative />
        </ItemMedia>
        <ItemContent>
          <ItemTitle>{p.name}</ItemTitle>
          <ItemDescription>{p.role}</ItemDescription>
        </ItemContent>
      </Item>
    ))}
  </ItemGroup>
</div>

With icons and actions

  • Q3 report.pdf
    2.4 MB, edited yesterday
  • Brand guide.pdf
    8.1 MB, edited 3 days ago
import { Button, FileIcon, Item, ItemActions, ItemContent, ItemDescription, ItemGroup, ItemMedia, ItemTitle } from "@rdloom/react";

const files = [
  { name: "Q3 report.pdf", meta: "2.4 MB, edited yesterday" },
  { name: "Brand guide.pdf", meta: "8.1 MB, edited 3 days ago" },
];

export default function ItemWithIconsAndActionsExample() {
  return (
    <div className="flex w-full justify-center">
      <ItemGroup aria-label="Files" className="max-w-md gap-2">
        {files.map((f) => (
          <Item key={f.name} variant="outline">
            <ItemMedia variant="icon">
              <FileIcon />
            </ItemMedia>
            <ItemContent>
              <ItemTitle>{f.name}</ItemTitle>
              <ItemDescription>{f.meta}</ItemDescription>
            </ItemContent>
            <ItemActions>
              <Button variant="secondary" size="sm">
                Download
              </Button>
            </ItemActions>
          </Item>
        ))}
      </ItemGroup>
    </div>
  );
}
import { Button, ChevronRightIcon, HomeIcon, Item, ItemActions, ItemContent, ItemDescription, ItemGroup, ItemMedia, ItemTitle, SettingsIcon } from "@rdloom/react";

export default function ItemAsLinksExample() {
  return (
    <div className="flex w-full justify-center">
      <ItemGroup aria-label="Workspace" className="max-w-md gap-2">
        <Item variant="outline" href="#overview">
          <ItemMedia variant="icon">
            <HomeIcon />
          </ItemMedia>
          <ItemContent>
            <ItemTitle>Overview</ItemTitle>
            <ItemDescription>Activity across all projects</ItemDescription>
          </ItemContent>
          <ItemActions>
            <ChevronRightIcon />
          </ItemActions>
        </Item>
        <Item variant="outline" href="#settings">
          <ItemMedia variant="icon">
            <SettingsIcon />
          </ItemMedia>
          <ItemContent>
            <ItemTitle>Settings</ItemTitle>
            <ItemDescription>Open the settings page, or reset it from here</ItemDescription>
          </ItemContent>
          <ItemActions>
            <Button variant="ghost" size="sm">
              Reset
            </Button>
          </ItemActions>
        </Item>
      </ItemGroup>
    </div>
  );
}

Grouped

  • Ada Lovelace
    ada@example.com
  • Grace Hopper
    grace@example.com
  • Katherine Johnson
    katherine@example.com
import { Fragment } from "react";
import { Avatar, Item, ItemContent, ItemDescription, ItemGroup, ItemMedia, ItemSeparator, ItemTitle } from "@rdloom/react";

const people = [
  { name: "Ada Lovelace", email: "ada@example.com" },
  { name: "Grace Hopper", email: "grace@example.com" },
  { name: "Katherine Johnson", email: "katherine@example.com" },
];

export default function ItemGroupedExample() {
  return (
    <div className="flex w-full justify-center">
      <ItemGroup aria-label="Members" className="max-w-sm">
        {people.map((p, i) => (
          <Fragment key={p.email}>
            {i > 0 ? <ItemSeparator /> : null}
            <Item size="sm">
              <ItemMedia>
                <Avatar name={p.name} size="sm" decorative />
              </ItemMedia>
              <ItemContent>
                <ItemTitle>{p.name}</ItemTitle>
                <ItemDescription>{p.email}</ItemDescription>
              </ItemContent>
            </Item>
          </Fragment>
        ))}
      </ItemGroup>
    </div>
  );
}

Muted

Billing moves to the first of the month
Your next invoice covers the days from today until then.
import { InfoIcon, Item, ItemContent, ItemDescription, ItemMedia, ItemTitle } from "@rdloom/react";

export default function ItemMutedExample() {
  return (
    <div className="flex w-full justify-center">
      <Item variant="muted" className="max-w-md">
        <ItemMedia variant="icon">
          <InfoIcon />
        </ItemMedia>
        <ItemContent>
          <ItemTitle>Billing moves to the first of the month</ItemTitle>
          <ItemDescription>Your next invoice covers the days from today until then.</ItemDescription>
        </ItemContent>
      </Item>
    </div>
  );
}

API Reference

Defined by the spec. Components also accept the props of the React Aria component they wrap.

PropTypeDefault
childrenrequired

ItemMedia, ItemContent and ItemActions. ItemActions is placed outside the link or button so it never nests one interactive element in another.

nodenone
variant

default has no surface, outline adds a border, muted adds a tinted background.

"default" | "outline" | "muted""default"
size

sm is a compact row, md the standard row.

"sm" | "md""md"
href

Makes the row a link to this address.

stringnone
onPress

Makes the row a button, or runs on a press of the link when href is also set.

(event: PressEvent) => voidnone
isDisabled

Disables the link or button part of the row. Actions keep their own state.

booleanfalse

Accessibility

Role listitem, WCAG 2.2 AA. Tested with axe and keyboard tests; screen reader checks are in the audit checklist.

Keyboard

  • Tab moves to the row when it is a link or button, then to each action
  • Enter activates a link row; Enter and Space activate a button row

Screen readers announce

  • A group is announced as a list with the number of items
  • A link row is announced as a link with its title and description
  • Each action is announced by its own label

What your code must do

  • ItemGroup is a list (role list) and each Item inside it is a listitem
  • A link or button row has a visible focus ring and a hover state
  • Actions are siblings of the link or button, never children of it, so no interactive element is nested in another
  • The link or button is named by its title and description; icon-only actions need a label
  • ItemMedia icons are decorative: the title carries the meaning
  • ItemSeparator is decorative inside a list and does not break the list count

Guidelines

Use it when

  • Lists of people, files, settings or notifications with an icon, text and an action
  • Rows that open a detail page
  • A compact stack of related options

Avoid it when

  • Rows of the same fields to compare: use a table or data grid
  • A large selectable collection: use a list box or grid list

Don't

  • Putting a button or link inside ItemContent when the row is already a link
  • Icon-only actions without a label
  • Using the muted variant for every row so none stands out

Design tokens

The semantic tokens this component uses. Change them once and every component follows; see Design tokens.

  • --rd-color-text-default
  • --rd-color-text-muted
  • --rd-color-border-default
  • --rd-color-surface-subtle
  • --rd-color-focus-ring
  • --rd-radius-control