Item
A row with media, a title, a description and actions, for lists of records and settings.
@zenoxai/ui/components/item MarkdownLast 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
No border, for rows inside a card.
A bordered row on the page surface.
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.
October 2026, PDF, 2.4 MB
Q3 2026, ZIP, 18 MB
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>
)
}As a link
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
ItemGrouphasrole="list", butItemhas no role of its own. Passrole="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-labelthat 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.
| Prop | Type | Default |
|---|---|---|
render | ReactElement<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" | nullDensity. `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.
| Prop | Type | Default |
|---|---|---|
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.
| Prop | Type | Default |
|---|---|---|
orientation | "horizontal" | "vertical"The orientation of the separator. | vertical |
render | ReactElement<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.