Item
Row layout for lists. Composes media, content, title, description, and action slots.
Examples
Avatar
Put a real Avatar (or a stack of them) inside ItemMedia — there's no dedicated avatar variant.
Last seen 5 months ago
Invite your team to collaborate on this project.
import { Avatar, AvatarFallback, AvatarImage } from "@codefast/ui/avatar";
import { Button } from "@codefast/ui/button";
import { Item, ItemActions, ItemContent, ItemDescription, ItemMedia, ItemTitle } from "@codefast/ui/item";
import { Plus } from "lucide-react";
export function ItemAvatar() {
return (
<div className="flex w-full max-w-lg flex-col gap-6">
<Item variant="outline">
<ItemMedia>
<Avatar className="size-10">
<AvatarImage src="https://images.unsplash.com/photo-1438761681033-6461ffad8d80?w=128&h=128&fit=crop&q=80" />
<AvatarFallback>AS</AvatarFallback>
</Avatar>
</ItemMedia>
<ItemContent>
<ItemTitle>Ava Stone</ItemTitle>
<ItemDescription>Last seen 5 months ago</ItemDescription>
</ItemContent>
<ItemActions>
<Button size="icon-sm" variant="outline" className="rounded-full" aria-label="Invite">
<Plus />
</Button>
</ItemActions>
</Item>
<Item variant="outline">
<ItemMedia>
<div className="flex -space-x-2 *:data-[slot=avatar]:ring-2 *:data-[slot=avatar]:ring-background *:data-[slot=avatar]:grayscale">
<Avatar className="hidden sm:flex">
<AvatarImage src="https://github.com/codefastlabs.png" alt="@codefast" />
<AvatarFallback>CF</AvatarFallback>
</Avatar>
<Avatar className="hidden sm:flex">
<AvatarImage
src="https://images.unsplash.com/photo-1500648767791-00dcc994a43e?w=128&h=128&fit=crop&q=80"
alt="@leo"
/>
<AvatarFallback>LP</AvatarFallback>
</Avatar>
<Avatar>
<AvatarImage
src="https://images.unsplash.com/photo-1438761681033-6461ffad8d80?w=128&h=128&fit=crop&q=80"
alt="@ava"
/>
<AvatarFallback>AS</AvatarFallback>
</Avatar>
</div>
</ItemMedia>
<ItemContent>
<ItemTitle>No Team Members</ItemTitle>
<ItemDescription>Invite your team to collaborate on this project.</ItemDescription>
</ItemContent>
<ItemActions>
<Button size="sm" variant="outline">
Invite
</Button>
</ItemActions>
</Item>
</div>
);
}
Dropdown
A versatile component for displaying content with media, title, description, and actions.
import { Avatar, AvatarFallback, AvatarImage } from "@codefast/ui/avatar";
import { Button } from "@codefast/ui/button";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuGroup,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@codefast/ui/dropdown-menu";
import { Item, ItemContent, ItemDescription, ItemMedia, ItemTitle } from "@codefast/ui/item";
import { ChevronDownIcon } from "lucide-react";
const people = [
{
username: "codefast",
avatar: "https://github.com/codefastlabs.png",
email: "hello@codefastlabs.com",
},
{
username: "leo",
avatar: "https://images.unsplash.com/photo-1500648767791-00dcc994a43e?w=128&h=128&fit=crop&q=80",
email: "leo@codefastlabs.com",
},
{
username: "ava",
avatar: "https://images.unsplash.com/photo-1438761681033-6461ffad8d80?w=128&h=128&fit=crop&q=80",
email: "ava@codefastlabs.com",
},
];
export function ItemDropdown() {
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline">
Select <ChevronDownIcon />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent className="w-48" align="end">
<DropdownMenuGroup>
{people.map((person) => (
<DropdownMenuItem key={person.username}>
<Item size="xs" className="w-full p-2">
<ItemMedia>
<Avatar className="size-[--spacing(6.5)]">
<AvatarImage src={person.avatar} className="grayscale" />
<AvatarFallback>{person.username.charAt(0)}</AvatarFallback>
</Avatar>
</ItemMedia>
<ItemContent className="gap-0">
<ItemTitle>{person.username}</ItemTitle>
<ItemDescription className="leading-none">{person.email}</ItemDescription>
</ItemContent>
</Item>
</DropdownMenuItem>
))}
</DropdownMenuGroup>
</DropdownMenuContent>
</DropdownMenu>
);
}
Group
Use ItemGroup to group related items together.
hello@codefastlabs.com
leo@codefastlabs.com
ava@codefastlabs.com
import { Avatar, AvatarFallback, AvatarImage } from "@codefast/ui/avatar";
import { Button } from "@codefast/ui/button";
import { Item, ItemActions, ItemContent, ItemDescription, ItemGroup, ItemMedia, ItemTitle } from "@codefast/ui/item";
import { PlusIcon } from "lucide-react";
const people = [
{
username: "codefast",
avatar: "https://github.com/codefastlabs.png",
email: "hello@codefastlabs.com",
},
{
username: "leo",
avatar: "https://images.unsplash.com/photo-1500648767791-00dcc994a43e?w=128&h=128&fit=crop&q=80",
email: "leo@codefastlabs.com",
},
{
username: "ava",
avatar: "https://images.unsplash.com/photo-1438761681033-6461ffad8d80?w=128&h=128&fit=crop&q=80",
email: "ava@codefastlabs.com",
},
];
export function ItemGroupExample() {
return (
<ItemGroup className="max-w-sm">
{people.map((person) => (
<Item key={person.username} variant="outline">
<ItemMedia>
<Avatar>
<AvatarImage src={person.avatar} className="grayscale" />
<AvatarFallback>{person.username.charAt(0)}</AvatarFallback>
</Avatar>
</ItemMedia>
<ItemContent className="gap-1">
<ItemTitle>{person.username}</ItemTitle>
<ItemDescription>{person.email}</ItemDescription>
</ItemContent>
<ItemActions>
<Button variant="ghost" size="icon" className="rounded-full">
<PlusIcon />
</Button>
</ItemActions>
</Item>
))}
</ItemGroup>
);
}
Header
Use ItemHeader to add a header above the item content.
Everyday tasks and UI generation.
Advanced thinking or reasoning.
Open Source model for everyone.
import { Item, ItemContent, ItemDescription, ItemGroup, ItemHeader, ItemTitle } from "@codefast/ui/item";
import { Image } from "@unpic/react";
const models = [
{
name: "v0-1.5-sm",
description: "Everyday tasks and UI generation.",
image: "https://images.unsplash.com/photo-1650804068570-7fb2e3dbf888?q=80&w=640&auto=format&fit=crop",
},
{
name: "v0-1.5-lg",
description: "Advanced thinking or reasoning.",
image: "https://images.unsplash.com/photo-1610280777472-54133d004c8c?q=80&w=640&auto=format&fit=crop",
},
{
name: "v0-2.0-mini",
description: "Open Source model for everyone.",
image: "https://images.unsplash.com/photo-1602146057681-08560aee8cde?q=80&w=640&auto=format&fit=crop",
},
];
export function ItemHeaderDemo() {
return (
<div className="flex w-full max-w-xl flex-col gap-6">
<ItemGroup className="grid grid-cols-3 gap-4">
{models.map((model) => (
<Item key={model.name} variant="outline">
<ItemHeader>
<Image
alt={model.name}
className="aspect-square w-full rounded-sm object-cover"
height={128}
layout="constrained"
src={model.image}
width={128}
/>
</ItemHeader>
<ItemContent>
<ItemTitle>{model.name}</ItemTitle>
<ItemDescription>{model.description}</ItemDescription>
</ItemContent>
</Item>
))}
</ItemGroup>
</div>
);
}
Icon
Use ItemMedia with variant='icon' to display an icon.
New login detected from unknown device.
import { Button } from "@codefast/ui/button";
import { Item, ItemActions, ItemContent, ItemDescription, ItemMedia, ItemTitle } from "@codefast/ui/item";
import { ShieldAlertIcon } from "lucide-react";
export function ItemIcon() {
return (
<div className="flex w-full max-w-lg flex-col gap-6">
<Item variant="outline">
<ItemMedia variant="icon">
<ShieldAlertIcon />
</ItemMedia>
<ItemContent>
<ItemTitle>Security Alert</ItemTitle>
<ItemDescription>New login detected from unknown device.</ItemDescription>
</ItemContent>
<ItemActions>
<Button size="sm" variant="outline">
Review
</Button>
</ItemActions>
</Item>
</div>
);
}
Image
Use ItemMedia with variant='image' to display an image.
import { Item, ItemContent, ItemDescription, ItemGroup, ItemMedia, ItemTitle } from "@codefast/ui/item";
import { Image } from "@unpic/react";
const music = [
{
title: "Midnight City Lights",
artist: "Neon Dreams",
album: "Electric Nights",
duration: "3:45",
art: "https://images.unsplash.com/photo-1465869185982-5a1a7522cbcb?auto=format&fit=crop&w=128&q=80",
},
{
title: "Coffee Shop Conversations",
artist: "The Morning Brew",
album: "Urban Stories",
duration: "4:05",
art: "https://images.unsplash.com/photo-1548516173-3cabfa4607e9?auto=format&fit=crop&w=128&q=80",
},
{
title: "Digital Rain",
artist: "Cyber Symphony",
album: "Binary Beats",
duration: "3:30",
art: "https://images.unsplash.com/photo-1494337480532-3725c85fd2ab?auto=format&fit=crop&w=128&q=80",
},
];
export function ItemImage() {
return (
<div className="flex w-full max-w-md flex-col gap-6">
<ItemGroup className="gap-4">
{music.map((song) => (
<Item key={song.title} variant="outline" asChild role="listitem">
<a href="/">
<ItemMedia variant="image">
<Image
alt={song.title}
className="object-cover grayscale"
height={32}
layout="fixed"
src={song.art}
width={32}
/>
</ItemMedia>
<ItemContent>
<ItemTitle className="line-clamp-1">
{song.title} - <span className="text-muted-foreground">{song.album}</span>
</ItemTitle>
<ItemDescription>{song.artist}</ItemDescription>
</ItemContent>
<ItemContent className="flex-none text-center">
<ItemDescription>{song.duration}</ItemDescription>
</ItemContent>
</a>
</Item>
))}
</ItemGroup>
</div>
);
}
Link
Use the asChild prop to render the item as a link. The hover and focus states will be applied to the anchor element.
import { Item, ItemActions, ItemContent, ItemDescription, ItemTitle } from "@codefast/ui/item";
import { ChevronRightIcon, ExternalLinkIcon } from "lucide-react";
export function ItemLink() {
return (
<div className="flex w-full max-w-md flex-col gap-4">
<Item asChild>
<a href="/">
<ItemContent>
<ItemTitle>Visit our documentation</ItemTitle>
<ItemDescription>Learn how to get started with our components.</ItemDescription>
</ItemContent>
<ItemActions>
<ChevronRightIcon className="size-4" />
</ItemActions>
</a>
</Item>
<Item variant="outline" asChild>
<a href="/" target="_blank" rel="noopener noreferrer">
<ItemContent>
<ItemTitle>External resource</ItemTitle>
<ItemDescription>Opens in a new tab with security attributes.</ItemDescription>
</ItemContent>
<ItemActions>
<ExternalLinkIcon className="size-4" />
</ItemActions>
</a>
</Item>
</div>
);
}
RTL
Right-to-left layout support for languages such as Arabic and Hebrew.
Translations are AI-generated for demonstration and may be imperfect.
عنصر بسيط يحتوي على عنوان ووصف.
import { Button } from "@codefast/ui/button";
import { Item, ItemActions, ItemContent, ItemDescription, ItemMedia, ItemTitle } from "@codefast/ui/item";
import { BadgeCheckIcon, ChevronRightIcon } from "lucide-react";
import type { Translations } from "#/features/components-catalog/components/detail/language";
import { useTranslation } from "#/features/components-catalog/components/detail/language-context";
const translations: Translations = {
en: {
dir: "ltr",
values: {
basicItem: "Basic Item",
basicItemDesc: "A simple item with title and description.",
action: "Action",
verifiedTitle: "Your profile has been verified.",
},
},
ar: {
dir: "rtl",
values: {
basicItem: "عنصر أساسي",
basicItemDesc: "عنصر بسيط يحتوي على عنوان ووصف.",
action: "إجراء",
verifiedTitle: "تم التحقق من ملفك الشخصي.",
},
},
he: {
dir: "rtl",
values: {
basicItem: "פריט בסיסי",
basicItemDesc: "פריט פשוט עם כותרת ותיאור.",
action: "פעולה",
verifiedTitle: "הפרופיל שלך אומת.",
},
},
};
export function ItemRtl() {
const { dir, t } = useTranslation(translations, "ar");
return (
<div className="flex w-full max-w-md flex-col gap-6" dir={dir}>
<Item variant="outline" dir={dir}>
<ItemContent>
<ItemTitle>{t.basicItem}</ItemTitle>
<ItemDescription>{t.basicItemDesc}</ItemDescription>
</ItemContent>
<ItemActions>
<Button variant="outline" size="sm">
{t.action}
</Button>
</ItemActions>
</Item>
<Item variant="outline" size="sm" asChild dir={dir}>
<a href="/">
<ItemMedia>
<BadgeCheckIcon className="size-5" />
</ItemMedia>
<ItemContent>
<ItemTitle>{t.verifiedTitle}</ItemTitle>
</ItemContent>
<ItemActions>
<ChevronRightIcon className="size-4" />
</ItemActions>
</a>
</Item>
</div>
);
}
Size
Use the size prop to change the size of the item.
The standard size for most use cases.
A compact size for dense layouts.
import { Item, ItemContent, ItemDescription, ItemMedia, ItemTitle } from "@codefast/ui/item";
import { InboxIcon } from "lucide-react";
export function ItemSizeDemo() {
return (
<div className="flex w-full max-w-md flex-col gap-6">
<Item variant="outline">
<ItemMedia variant="icon">
<InboxIcon />
</ItemMedia>
<ItemContent>
<ItemTitle>Default Size</ItemTitle>
<ItemDescription>The standard size for most use cases.</ItemDescription>
</ItemContent>
</Item>
<Item variant="outline" size="sm">
<ItemMedia variant="icon">
<InboxIcon />
</ItemMedia>
<ItemContent>
<ItemTitle>Small Size</ItemTitle>
<ItemDescription>A compact size for dense layouts.</ItemDescription>
</ItemContent>
</Item>
<Item variant="outline" size="xs">
<ItemMedia variant="icon">
<InboxIcon />
</ItemMedia>
<ItemContent>
<ItemTitle>Extra Small Size</ItemTitle>
<ItemDescription>The most compact size available.</ItemDescription>
</ItemContent>
</Item>
</div>
);
}
Variant
Use the variant prop to change the visual style of the item.
Transparent background with no border.
Outlined style with a visible border.
Muted background for secondary content.
import { Item, ItemContent, ItemDescription, ItemMedia, ItemTitle } from "@codefast/ui/item";
import { InboxIcon } from "lucide-react";
export function ItemVariant() {
return (
<div className="flex w-full max-w-md flex-col gap-6">
<Item>
<ItemMedia variant="icon">
<InboxIcon />
</ItemMedia>
<ItemContent>
<ItemTitle>Default Variant</ItemTitle>
<ItemDescription>Transparent background with no border.</ItemDescription>
</ItemContent>
</Item>
<Item variant="outline">
<ItemMedia variant="icon">
<InboxIcon />
</ItemMedia>
<ItemContent>
<ItemTitle>Outline Variant</ItemTitle>
<ItemDescription>Outlined style with a visible border.</ItemDescription>
</ItemContent>
</Item>
<Item variant="muted">
<ItemMedia variant="icon">
<InboxIcon />
</ItemMedia>
<ItemContent>
<ItemTitle>Muted Variant</ItemTitle>
<ItemDescription>Muted background for secondary content.</ItemDescription>
</ItemContent>
</Item>
</div>
);
}
Usage
The minimal import and composition — see Examples below for styled, real-world variants.
import { Item, ItemContent, ItemDescription, ItemTitle } from "@codefast/ui/item";
export function ItemUsage() {
return (
<Item variant="outline">
<ItemContent>
<ItemTitle>Basic Item</ItemTitle>
<ItemDescription>A simple item with a title and description.</ItemDescription>
</ItemContent>
</Item>
);
}
Anatomy
How the parts nest — every slot the component exposes, in composition order.
Features
- Three visual variants (default, muted, outline) and three sizes (default, sm, xs).
- ItemMedia has icon and image variants with matching size scaling — wrap a real Avatar inside for an avatar row instead of a dedicated variant.
- asChild renders the whole Item as its child (e.g. a link) so hover/focus states apply to the real interactive element.
API reference
Props for each part of the component. All native element props are also forwarded.
Item
A single row; can render as a real interactive element via asChild.
variant"default" | "muted" | "outline"Visual style of the row.
Default
"default"size"default" | "sm" | "xs"Row density.
Default
"default"asChildbooleanRender as its child (e.g. an anchor) instead of a div.
Default
falsechildrenReactNodeCompose ItemMedia, ItemContent, and ItemActions inside an Item.
ItemGroup
Layout container that stacks Item rows with role='list'.
childrenReactNodeOne or more Item rows.
ItemMedia
Leading visual slot — an icon, image, or a real Avatar.
variant"default" | "icon" | "image"Sizes and scales the media to match icon or image content.
Default
"default"childrenReactNodeAn icon, image, or Avatar element.
ItemContent
Flexible column holding the title and description.
childrenReactNodeItemTitle and ItemDescription.
ItemTitle
The row's primary label.
childrenReactNodeTitle text, truncated to a single line.
ItemDescription
Secondary text below the title.
childrenReactNodeSupporting text, clamped to two lines.
ItemActions
Trailing slot for controls such as buttons or a dropdown trigger.
childrenReactNodeOne or more action elements.
Accessibility
Built to be keyboard-navigable and screen-reader friendly out of the box.
- Item is presentational — give actions real buttons/links with labels.
- Use ItemSeparator between rows; it’s decorative for assistive tech.
- If a whole row is clickable, wrap it in one link rather than nesting interactives.
Guidelines
Conventions that keep usage consistent across an app.
Do
- Use for settings lists, inboxes, and search results.
- Keep one primary action per row.
Don’t
- Don’t nest multiple competing actions in a row.
- Don’t use Item where a Table’s columns fit better.
Explore further
Ready to integrate?
Follow the Getting Started guide to install @codefast/ui, or browse the full component gallery.