Skip to content

shadcn.io is an independent project built on shadcn/ui

Free

Item Group for Ark UI

Three people in one group, divided by separators. Each row has an avatar, a username, a masked email and a round plus button.

Three rows in one group, with a separator between them. Each row has an avatar, a username, a masked email and a round plus button.

Group rows when they are one list, such as people you can invite. A Table fits the same people once you need columns. In React, Ark UI does not load the avatars.

Preview

Preview width

Installation

Sign in to get your install command
Free with an account. It carries your token, so the CLI installs everything in one go.
Use email insteadNo credit card

Anatomy

Item anatomy: root, media, title, description, actions

Usage

<div className="flex w-full max-w-md flex-col gap-6">
  <ItemGroup>
    {PEOPLE.map((person, index) => (
      <Fragment key={person.username}>
        <Item role="listitem">
          <ItemMedia>
            <Avatar>
              <AvatarImage src={person.avatar} />
              <AvatarFallback>{person.username.charAt(0)}</AvatarFallback>
            </Avatar>
          </ItemMedia>
          <ItemContent className="gap-1">
            <ItemTitle>{person.username}</ItemTitle>
            <ItemDescription>{person.email}</ItemDescription>
          </ItemContent>
          <ItemActions>
            <Button
              aria-label={`Invite ${person.username}`}
              pill
              size="icon-md"
              variant="ghost"
            >
              <PlusIcon aria-hidden="true" />
            </Button>
          </ItemActions>
        </Item>
        {index !== PEOPLE.length - 1 && <ItemSeparator />}
      </Fragment>
    ))}
  </ItemGroup>
</div>

FAQ

When should Items go in an ItemGroup?

Whenever several rows form one list. ItemGroup stacks them with a gap that follows their size and gives the block the list role, which is why each row here has role="listitem". A single row on its own doesn't need a group.

How does ItemSeparator work between Items?

ItemSeparator is a horizontal Separator with a little vertical margin. Render it between rows, skipping the last index, so the list doesn't end with a stray rule. For rows that already have borders, as in the outline variant, you can drop the separators and rely on the group's gap.

How does the loop render a row and a separator together?

Each pass of the loop renders two siblings, the Item and its separator, keyed by the username, with no element wrapped around them. An extra wrapper would sit between the list and its items and break the layout. A Fragment carries the key without adding anything to the DOM.

What actions suit a grouped list?

One small action per row works best, like the icon button here. Use the icon-md size, pill for a round shape and the ghost variant so the buttons don't outweigh the names. An icon-only button needs an aria-label that names the row, such as Invite haydenbleasel.

Should every Item in a group have the same media?

Yes: matching media keeps titles lined up down the list. If a person has no picture, AvatarFallback shows the first letter of the username in the same size, so the row still lines up.

How do screen readers read an ItemGroup?

The group is announced as a list and each row as a list item, so people hear how many entries there are. Each button's aria-label names its person, so moving from button to button doesn't just repeat Invite.

API Reference

Item

Sets role="listitem". The rest is on the Item page.

Avatar

Sets src={person.avatar}. The rest is on the Avatar page.

Button

Sets aria-label, Invite, person, username, pill and size="icon-md". The rest is on the Button page.