---
title: Item
description: One row of a list: tile, title, description, meta - the nav panel's anatomy, extracted for reuse.
source: https://adamjurek.com/components/item
---

## Home


One row of a list: an optional toned icon tile, a title, a fainter
description line, and a right-aligned meta label, rendered as a link when
`href` is given. Reach for it for a settings row, a changelog entry or any
list whose rows share that shape.

<!-- ::start:showcase demo="item" height="380" -->
<!-- ::end:showcase -->

## Why it is built this way

The nav panel and the command palette had already drawn this exact anatomy
before `Item` existed, so extracting it stops a settings page or a changelog
from rebuilding a slightly different version a third time. `tone` colours the
icon tile through `data-tone` and does nothing without `icon`, because the
tile is the only thing it was ever meant to colour.

## What it does not do

It draws one row and nothing that manages many of them: no list wrapper, no
active-item tracking, no keyboard navigation between rows. Stack several
inside a `<ul>` or a flex column and that stays the consumer's concern, which
is what lets `Item` sit next to the nav panel or the palette without
depending on either.

> [!NOTE] Install commands are not written here
> Anything in `packages/ui/registry.ts` gets its TanStack and shadcn commands
> attached automatically, so there is nothing to keep in sync.


## Install

<!-- ::start:tabs -->

### TanStack

```shell
tanstack add https://adamjurek.com/r/tanstack/item.json
```

### shadcn

```shell
pnpm dlx shadcn@latest add https://adamjurek.com/r/shadcn/item.json
```

### pnpm

```shell
pnpm add @sushindustries/ui @sushindustries/atoms
```

<!-- ::end:tabs -->

## What you get

| | |
| --- | --- |
| Version | 0.1.0 |
| Category | layout · Containers |
| Files | `item.tsx` |
| Dependencies | None |
| Also installs | `icon` |
| Tags | list, row, no-deps |

> [!NOTE] No runtime dependencies
> It brings nothing with it beyond the stylesheet.

## Get Started


Install commands are on Home, attached from the registry - they are not written
here, because a second copy is a copy that goes stale. This tab starts after the
install worked.

## Use it

```tsx
import { Item } from "@sushindustries/ui";

export function Example() {
	return (
		<Item
			title="Grid"
			description="A responsive grid with no breakpoints in it."
			meta="ui"
			icon="grid"
			tone="layout"
			href="/packages/ui/docs/grid"
		/>
	);
}
```

## What you should see

One row: a small toned tile with the grid glyph on the left, the title in
bold above a fainter description line, and "ui" right-aligned. The whole row
is a link, because `href` was given - drop it and the same row renders as a
plain `<div>` instead.

## If nothing happens

`tone` does nothing without `icon` - the tile, and the only thing `tone`
colors, is not rendered at all when `icon` is left unset. If the row shows
text but no tile where you expected one, check `icon` is set first.


## Guides


The Guides tab is for the things that are true after it works. If it belongs in
"how do I install this", it goes in Get Started; if it is a prop table, it goes
in API.

## Composing it

`Item` draws one row and assumes nothing about what holds the rows - stack
several inside a `<ul>` or a plain flex column and each one lays itself out
the same way regardless. It is the anatomy the nav panel and the command
palette already use, so a list of `Item`s next to either will match without
any extra styling.

## Variants

`tone` is a real prop, not a placeholder - it writes `data-tone` on the icon
tile, and the stylesheet defines five: `motion`, `layout`, `content`, `docs`
and `3d`. Leaving it unset draws a neutral gray tile rather than no tile at
all.

```tsx
<Item title="Grid" icon="grid" tone="layout" />
```

There is no sixth tone to reach for informally - a new one is a new pair of
tokens in `atoms`, not a hex value passed through `className`.


## API


<!-- generated:api -->

## Props

| Prop | Type | Default | Does |
| --- | --- | --- | --- |
| `title` | `string` | - |  |
| `description?` | `string` | - | One line under the title. |
| `meta?` | `string` | - | Right-aligned, in the label style. |
| `icon?` | `IconName` | - | Draws the tile at the left. Without it the row starts at the title. |
| `tone?` | `string` | - | Colour family for the tile, resolved by the stylesheet. Does nothing without `icon`. |
| `href?` | `string` | - | Renders the row as a link. |

<!-- /generated:api -->

## Notes

`title`, `description` and `meta` are all truncated with an ellipsis rather
than wrapped - a row is one line, always, whatever the container width. Set
`href` and the whole row becomes the link; there is no way to make only part
of the row - just the title, say - clickable without `href` on the row as a
whole.


## Examples


Examples are the tab where the component is shown doing a job, not
demonstrating a prop. The API tab already lists the props.

<!-- ::start:showcase demo="item" height="420" -->
<!-- ::end:showcase -->

Press Compare. The frames are real viewports, so a layout that breaks at 320
breaks here too rather than in somebody's hands.

## In a page

```tsx
import { Item } from "@sushindustries/ui";

const packages = [
	{ name: "ui", tone: "layout", summary: "The components the site is made of." },
	{ name: "atoms", tone: "content", summary: "Design tokens and atomic CSS." },
	{ name: "db", tone: "docs", summary: "Drizzle schema and client." },
] as const;

export function PackageList() {
	return (
		<ul className="flex flex-col gap-1">
			{packages.map((pkg) => (
				<li key={pkg.name}>
					<Item
						title={pkg.name}
						description={pkg.summary}
						icon="package"
						tone={pkg.tone}
						href={`/packages/${pkg.name}`}
					/>
				</li>
			))}
		</ul>
	);
}
```
