A card-per-row list with a leading icon, auto-derived columns, inline actions, and click-through navigation.
npx shadcn@latest add https://sdk-components.thesqd.com/r/item-list.jsonimport { ItemList, type ItemListRow } from "@/components/ui/item-list";import { ItemList, type ItemListRow } from "@/components/ui/item-list";
import { GlobeIcon } from "lucide-react";
const data: ItemListRow[] = [
{
icon: GlobeIcon,
title: "Chrome",
subtitle: "Web Browser",
member: "Ann Wong",
amount: "$88,904.92",
href: "/apps/chrome",
},
];
export function Example() {
// Columns auto-derive from the keys (member, amount); pass `columns` to
// reorder / relabel / align / custom-render.
return <ItemList data={data} />;
}ItemList (props) // flat, card-per-row list
├── data: ItemListRow[] // each row is a dict
│ ├── icon? | image? // leading visual
│ ├── iconClassName? // e.g. "bg-transparent" for bare brand marks
│ ├── title / subtitle? // leading text
│ ├── [key]: ReactNode // any other key → a data column (auto)
│ ├── actions?: { icon, label, onClick }[] // animate-ui icon buttons
│ └── href? | onNavigate? // trailing chevron navigation
├── variant?: "card" | "flat" // card rows (default) or hairline rows
├── columns?: ItemListColumn[] // optional: reorder / relabel / align / render
├── nameHeader? (default "Name")
├── sortable? // click a header: asc → desc → unsorted
├── sort? / defaultSort? / onSortChange? // controlled or uncontrolled sort
├── nameSortValue? // what the name column sorts by
└── hideHeader? / emptyState? / getRowId?
Row = <Card> surface
├── Leading cell — icon|image + title + subtitle
├── Data columns — value rendered as-is (drop in Badge / Select / Input to edit inline)
├── Action cluster — animated Lucide icons, animate on hover
└── Chevron — navigates via href (<a>) or onNavigate



ItemList
| Prop | Type | Default | Description |
|---|---|---|---|
data | ItemListRow[] | — | Rows to render. Each is a dict; non-reserved keys become data columns automatically. |
variant | "card" | "flat" | "card" | `card` wraps each row in a Card. `flat` drops the cards for hairline-split rows, wide 16:10 thumbnails, and muted columns. |
columns | ItemListColumn[] | — | Optional override — reorder, relabel, align, set width, or custom-render columns. Omit to auto-derive from keys. |
nameHeader | ReactNode | "Name" | Header label for the leading (icon + title) column. |
hideHeader | boolean | false | Hide the uppercase column-label header row. |
sortable | boolean | false | Make every column header a button that sorts the rows: ascending, then descending, then back to the order of `data`. Blank values always go last. A column can opt out with its own `sortable: false`. |
sort | { key: string; direction: "asc" | "desc" } | null | — | Controlled sort. `key` is a column key, or `"title"` for the name column; `null` is unsorted. |
defaultSort | { key: string; direction: "asc" | "desc" } | null | — | The starting sort when `sort` is not passed. |
onSortChange | (sort) => void | — | Called with the new sort (or `null`) when a header is clicked. |
nameSortValue | (row) => string | number | boolean | Date | null | — | What the name column sorts by. Defaults to `title` when it is text. |
getRowId | (row, index) => string | — | Stable row key. Falls back to `row.id` then the index. |
emptyState | ReactNode | — | Rendered in place of the list when `data` is empty. |
aria-label | string | — | Accessibility label for the list. |
className | string | — | Extra wrapper classes. |
ItemListRow
| Prop | Type | Default | Description |
|---|---|---|---|
id | string | number | — | Stable id (else `getRowId`, else the index). |
icon | ComponentType<{ className?: string }> | — | Leading icon (use this OR `image`). |
iconClassName | string | — | Classes merged onto the icon's chip. Pass `bg-transparent` for marks that bring their own shape (brand logos). |
image | string | — | Leading image URL (use this OR `icon`). |
imageAlt | string | — | Alt text for `image`. |
title | ReactNode | — | Primary label in the leading cell. |
subtitle | ReactNode | — | Secondary muted label under the title. |
actions | ItemListAction[] | — | Animated icon-action buttons rendered before the chevron. |
href | string | — | Navigate here when the chevron is clicked (renders an `<a>`). |
target | HTMLAttributeAnchorTarget | — | Anchor target for `href`. |
onNavigate | (row) => void | — | Alternative to `href` — called when the chevron is clicked. |
[key] | ReactNode | — | Any other key becomes a data column. The value may be any node — text, a Badge, a Select, an Input, etc. |
ItemListColumn
| Prop | Type | Default | Description |
|---|---|---|---|
key | string | — | Row key this column reads. |
header | ReactNode | — | Header label. Defaults to a humanized version of `key`. |
align | "start" | "center" | "end" | "start" | Text alignment for the column. |
width | string | number | — | Track width (number = px). Defaults to a flexible `1fr`. |
render | (value, row) => ReactNode | — | Custom cell renderer. Without it the raw value renders as-is. |
className | string | — | Extra classes on the cell. |
sortable | boolean | — | Override the list's `sortable` for this column. |
sortValue | (row) => string | number | boolean | Date | null | — | What this column sorts by. Text, numbers, booleans and Dates sort as themselves; give a column of nodes (a Badge, a formatted date) its value here. |
ItemListAction
| Prop | Type | Default | Description |
|---|---|---|---|
icon | AnimatedIconComponent | — | Animated (motion) Lucide icon — e.g. `FoldersIcon` from `@/components/ui/folders`. Plays its animation on hover. |
label | string | — | Accessible label + tooltip. |
onClick | (e) => void | — | Fires when the action is clicked. |
disabled | boolean | — | Disables the action. |