Skip to content
UI

Item

A row with media, a title, a description and actions, for lists of records and settings.

@zenoxai/ui/components/item Markdown
Okta connector

Last sync 4 minutes ago

<Item variant="outline" className="max-w-md">
  <ItemMedia variant="icon">
    <PlugIcon />
  </ItemMedia>
  <ItemContent>
    <ItemTitle>Okta connector</ItemTitle>
    <ItemDescription>Last sync 4 minutes ago</ItemDescription>
  </ItemContent>
  <ItemActions>
    <Button variant="outline" size="sm">
      Configure
    </Button>
  </ItemActions>
</Item>

Usage

import { PlugIcon } from "lucide-react"

import { Button } from "@zenoxai/ui/components/button"
import {
  Item,
  ItemActions,
  ItemContent,
  ItemDescription,
  ItemMedia,
  ItemTitle,
} from "@zenoxai/ui/components/item"

export function ConnectorRow() {
  return (
    <Item variant="outline">
      <ItemMedia variant="icon">
        <PlugIcon />
      </ItemMedia>
      <ItemContent>
        <ItemTitle>Okta connector</ItemTitle>
        <ItemDescription>Last sync 4 minutes ago</ItemDescription>
      </ItemContent>
      <ItemActions>
        <Button variant="outline" size="sm">
          Configure
        </Button>
      </ItemActions>
    </Item>
  )
}

Use an item for a short list of records the user reads one by one: connectors, reports, on-call analysts, recent activity. ItemTitle clamps to one line and ItemDescription to two, so long names never push the actions out of the row.

Use something else when:

  • The records have several attributes to compare across rows. Use a Table.
  • The content is a whole panel with its own header and footer. Use a Card.

Variants

Default

No border, for rows inside a card.

Outline

A bordered row on the page surface.

Muted

A tinted row that stands apart.

<div className="grid w-full max-w-md gap-3">
  <Item>
    <ItemContent>
      <ItemTitle>Default</ItemTitle>
      <ItemDescription>No border, for rows inside a card.</ItemDescription>
    </ItemContent>
  </Item>
  <Item variant="outline">
    <ItemContent>
      <ItemTitle>Outline</ItemTitle>
      <ItemDescription>A bordered row on the page surface.</ItemDescription>
    </ItemContent>
  </Item>
  <Item variant="muted">
    <ItemContent>
      <ItemTitle>Muted</ItemTitle>
      <ItemDescription>A tinted row that stands apart.</ItemDescription>
    </ItemContent>
  </Item>
</div>
Variant Use for
default Rows inside a card or a popover, where the container has the border.
outline Rows on the page surface, each with its own border.
muted A row that should stand apart, such as a pinned or suggested record.

In a list

ItemGroup stacks items and renders role="list". Give each Item role="listitem" so the list structure reaches assistive technology. size="sm" tightens the gap between items and shrinks image media; size="xs" fits menus and popovers.

Monthly exposure report

October 2026, PDF, 2.4 MB

PCI DSS evidence pack

Q3 2026, ZIP, 18 MB

Phishing simulation results

September 2026, CSV, 96 KB

function ItemList() {
  const reports = [
    { name: "Monthly exposure report", meta: "October 2026, PDF, 2.4 MB" },
    { name: "PCI DSS evidence pack", meta: "Q3 2026, ZIP, 18 MB" },
    { name: "Phishing simulation results", meta: "September 2026, CSV, 96 KB" },
  ]

  return (
    <ItemGroup className="max-w-md">
      {reports.map((report) => (
        <Item key={report.name} role="listitem" variant="outline" size="sm">
          <ItemMedia variant="icon">
            <FileTextIcon />
          </ItemMedia>
          <ItemContent>
            <ItemTitle>{report.name}</ItemTitle>
            <ItemDescription>{report.meta}</ItemDescription>
          </ItemContent>
          <ItemActions>
            <Button
              variant="ghost"
              size="icon-sm"
              aria-label={`Download ${report.name}`}
            >
              <DownloadIcon />
            </Button>
          </ItemActions>
        </Item>
      ))}
    </ItemGroup>
  )
}

An item that opens a record renders an <a> through the render prop, with the content inside the link. It gets a hover background and a focus ring.

<Item
  variant="outline"
  className="max-w-md"
  render={
    <a href="#usage">
      <ItemMedia variant="icon">
        <ShieldAlertIcon />
      </ItemMedia>
      <ItemContent>
        <ItemTitle>7 critical alerts need triage</ItemTitle>
        <ItemDescription>Oldest opened 3 hours ago</ItemDescription>
      </ItemContent>
      <ItemActions>
        <ChevronRightIcon className="size-4" />
      </ItemActions>
    </a>
  }
/>

Accessibility

  • ItemGroup has role="list", but Item has no role of its own. Pass role="listitem" to every item in a group, as in the example above; without it, assistive technology finds a list with no items.
  • An icon-only action needs an aria-label that names the record: “Download PCI DSS evidence pack”, not “Download”.
  • An item rendered as a link takes its accessible name from all of its text. Keep the title first and the description short.

API reference

Generated from the TypeScript source. className, style and children are accepted everywhere and omitted below.

Item

Item is a row with optional media, a title and description, and actions: a list entry, a setting, a linked record. Compose ItemMedia, ItemContent (ItemTitle, ItemDescription) and ItemActions. For data in columns use Table; for a standalone boxed section, Card.

PropTypeDefault
renderReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, {}>

Allows you to replace the component's HTML element with a different tag, or compose it with another component.

-
size"default" | "xs" | "sm" | null

Density. `sm` tightens the vertical padding (8px instead of 10px); `sm` and `xs` shrink an image ItemMedia (40, 32 and 24px) and the gap between Items in an ItemGroup; `xs` also tightens the padding and drops it inside a dropdown menu.

default
variant"default" | "outline" | "muted" | null

`default` has no border or fill; `outline` adds a border; `muted` a light fill.

default

ItemMedia

ItemMedia is the leading icon, avatar or image of an Item. When the Item has a description, it aligns with the title instead of the middle.

PropTypeDefault
variant"image" | "default" | "icon" | null

`default` shows the media as it is; `icon` sizes an icon to 16px; `image` crops an image to a square that follows the Item `size`.

default

ItemContent

ItemContent is the text column of an Item, holding ItemTitle and ItemDescription; it takes the remaining width.

No props of its own. Accepts the props of the element it renders.

ItemActions

ItemActions holds the buttons at the end of the row.

No props of its own. Accepts the props of the element it renders.

ItemGroup

ItemGroup stacks Items as a list with role="list". Items do not set a role of their own: give each one role="listitem".

No props of its own. Accepts the props of the element it renders.

ItemSeparator

ItemSeparator is a horizontal rule between Items in an ItemGroup.

PropTypeDefault
orientation"horizontal" | "vertical"

The orientation of the separator.

vertical
renderReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, SeparatorState>

Allows you to replace the component's HTML element with a different tag, or compose it with another component.

-

ItemTitle

ItemTitle is the main text of an Item, clamped to one line.

No props of its own. Accepts the props of the element it renders.

ItemDescription

ItemDescription is the text under the title, clamped to two lines.

No props of its own. Accepts the props of the element it renders.

ItemHeader

ItemHeader is a full-width row above the Item's main row, its children pushed to both ends.

No props of its own. Accepts the props of the element it renders.

ItemFooter

ItemFooter is a full-width row below the Item's main row, its children pushed to both ends.

No props of its own. Accepts the props of the element it renders.