GitHub

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 item
01

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

PropTypeDefaultDescription
iconReactNodeLeading icon slot.
labelstringPrimary text content.
descriptionstringSecondary text below the label.
trailingReactNodeTrailing slot for badges, shortcuts, or action buttons.
activebooleanfalseHighlights the item with a primary background.
disabledbooleanfalsePrevents interaction and reduces opacity.
classNamestringExtra CSS classes merged via cn().
React Principles