Item
A composable list item primitive with slots for icon, label, description, and trailing action. Foundation for list UIs, command palettes, and menu-like layouts.
AccessibleDark ModeActive StateSlot-based
Install
$
npx react-principles add item01
Features
- ✓Four independent slots: icon, label, description, and trailing action
- ✓Active state: visually distinct highlighting for selected items
- ✓Disabled state: prevents interaction and reduces opacity
- ✓Composable slots: icon, label, description, and trailing are all independently optional
- ✓Accessible:
role="option",aria-selected,aria-disabled
02
Live Demo
All slots populated
searchSearchFind files and content⌘K
homeHomeGo to dashboard
logoutSign out
With trailing badges
mailMessages3
updateUpdatesNew
Minimal — label only
Plain item
Another item
03
Code Snippet
src/ui/Item.tsx
import { Item } from "@/ui/Item"; import { Kbd } from "@/ui/Kbd"; // Basic <Item label="Settings" /> // With icon <Item label="Search" icon={<span className="material-symbols-outlined text-[18px]">search</span>} /> // With description <Item label="Notifications" description="Manage your notification preferences" /> // With trailing action <Item label="Save" trailing={<Kbd>⌘S</Kbd>} /> // Active state <Item label="Home" active /> // Disabled <Item label="Sign out" disabled />
04
Copy-Paste (Single File)
Item.tsx
import { type HTMLAttributes, type ReactNode, } from "react"; import { cn } from "@/lib/utils"; export interface ItemProps extends HTMLAttributes<HTMLDivElement> { icon?: ReactNode; label: string; description?: string; trailing?: ReactNode; active?: boolean; disabled?: boolean; } export function Item({ icon, label, description, trailing, active = false, disabled = false, className, ...props }: ItemProps) { return ( <div role="option" aria-selected={active || undefined} aria-disabled={disabled || undefined} className={cn( "flex items-center gap-3 rounded-lg px-3 py-2 transition-colors", !disabled && "cursor-pointer", active && "bg-primary/10", !active && !disabled && "hover:bg-slate-100 dark:hover:bg-slate-800/50", disabled && "opacity-50 cursor-not-allowed pointer-events-none", className, )} {...props} > {icon && ( <span className="shrink-0 text-slate-500 dark:text-slate-400"> {icon} </span> )} <span className="min-w-0 flex-1"> <span className={cn( "block truncate text-sm font-medium", active ? "text-primary" : "text-slate-900 dark:text-white", )} > {label} </span> {description && ( <span className="mt-0.5 block truncate text-xs text-slate-500 dark:text-slate-400"> {description} </span> )} </span> {trailing && ( <span className="ml-auto shrink-0">{trailing}</span> )} </div> ); }
05
Usage Examples
Command palette item
<Item icon={<span className="material-symbols-outlined text-[18px]">search</span>} label="Search" description="Find files and content" trailing={<Kbd>⌘K</Kbd>} />
Menu with active and disabled items
<Item icon={<Icon />} label="Dashboard" active /> <Item icon={<Icon />} label="Settings" /> <Item icon={<Icon />} label="Sign out" disabled />
List with badge trailing
<Item icon={<Icon />} label="Messages" trailing={<Badge variant="default">3</Badge>} />
06
Props
| Prop | Type | Default | Description |
|---|---|---|---|
icon | ReactNode | — | Leading icon slot. |
label | string | — | Primary text content. |
description | string | — | Secondary text below the label. |
trailing | ReactNode | — | Trailing slot for badges, shortcuts, or action buttons. |
active | boolean | false | Highlights the item with a primary background. |
disabled | boolean | false | Prevents interaction and reduces opacity. |
className | string | — | Extra CSS classes merged via cn(). |