2.4 MB · Uploaded by Ada Park
8.1 MB · Uploaded by Sam Rivera
import { FileTextIcon } from "lucide-react";
import { Button } from "@/components/ui/button";
import {
Item,
ItemActions,
ItemContent,
ItemDescription,
ItemGroup,
ItemMedia,
ItemTitle,
} from "@/components/ui/item";
export default function ItemVariants() {
return (
<ItemGroup className="max-w-lg">
<Item>
<ItemMedia variant="icon">
<FileTextIcon />
</ItemMedia>
<ItemContent>
<ItemTitle>Quarterly report.pdf</ItemTitle>
<ItemDescription>2.4 MB · Uploaded by Ada Park</ItemDescription>
</ItemContent>
<ItemActions>
<Button variant="secondary" size="sm">
Download
</Button>
</ItemActions>
</Item>
<Item variant="outline">
<ItemMedia variant="icon">
<FileTextIcon />
</ItemMedia>
<ItemContent>
<ItemTitle>Brand guidelines.pdf</ItemTitle>
<ItemDescription>8.1 MB · Uploaded by Sam Rivera</ItemDescription>
</ItemContent>
<ItemActions>
<Button variant="secondary" size="sm">
Download
</Button>
</ItemActions>
</Item>
</ItemGroup>
);
}- Add the registryOnce per project
pnpm dlx shadcn@latest registry add @jarvis=https://ui.jarv.is/r/{name}.json - Add item
pnpm dlx shadcn@latest add @jarvis/item
Usage
import { FileTextIcon } from "lucide-react";
import { Button } from "@/components/ui/button";
import {
Item,
ItemActions,
ItemContent,
ItemDescription,
ItemFooter,
ItemGroup,
ItemHeader,
ItemMedia,
ItemSeparator,
ItemTitle,
} from "@/components/ui/item";
export function FileList() {
return (
<ItemGroup>
<Item variant="outline">
<ItemHeader>Shared with you</ItemHeader>
<ItemMedia variant="icon">
<FileTextIcon />
</ItemMedia>
<ItemContent>
<ItemTitle>Quarterly report.pdf</ItemTitle>
<ItemDescription>2.4 MB</ItemDescription>
</ItemContent>
<ItemActions>
<Button variant="secondary" size="sm">
Download
</Button>
</ItemActions>
<ItemFooter>Updated yesterday</ItemFooter>
</Item>
<ItemSeparator />
<Item variant="outline">
<ItemContent>
<ItemTitle>Brand guidelines.pdf</ItemTitle>
</ItemContent>
</Item>
</ItemGroup>
);
}Long titles and descriptions are truncated.
Composition
Examples
Sizes
size sets the padding and gaps.
sam@example.com
sam@example.com
import { Badge } from "@/components/ui/badge";
import {
Item,
ItemActions,
ItemContent,
ItemDescription,
ItemGroup,
ItemMedia,
ItemTitle,
} from "@/components/ui/item";
import { people } from "@/examples/data";
export default function ItemSizes() {
const sam = people[1];
return (
<ItemGroup className="max-w-lg">
<Item variant="outline">
<ItemMedia variant="image">
<img src={sam.image} alt="" />
</ItemMedia>
<ItemContent>
<ItemTitle>{sam.name}</ItemTitle>
<ItemDescription>{sam.email}</ItemDescription>
</ItemContent>
<ItemActions>
<Badge variant="muted">Admin</Badge>
</ItemActions>
</Item>
<Item variant="outline" size="sm">
<ItemMedia variant="image">
<img src={sam.image} alt="" />
</ItemMedia>
<ItemContent>
<ItemTitle>{sam.name}</ItemTitle>
<ItemDescription>{sam.email}</ItemDescription>
</ItemContent>
<ItemActions>
<Badge variant="muted">Admin</Badge>
</ItemActions>
</Item>
<Item variant="outline" size="xs">
<ItemMedia variant="image">
<img src={sam.image} alt="" />
</ItemMedia>
<ItemContent>
<ItemTitle>{sam.name}</ItemTitle>
</ItemContent>
<ItemActions>
<Badge variant="muted">Admin</Badge>
</ItemActions>
</Item>
</ItemGroup>
);
}As a link
Pass an element to render to make the whole item a link or a button.
import { ChevronRightIcon, RocketIcon } from "lucide-react";
import {
Item,
ItemActions,
ItemContent,
ItemDescription,
ItemMedia,
ItemTitle,
} from "@/components/ui/item";
export default function ItemLink() {
return (
<Item variant="outline" className="max-w-lg" render={<a href="#production" />}>
<ItemMedia variant="icon">
<RocketIcon />
</ItemMedia>
<ItemContent>
<ItemTitle>Production deployment</ItemTitle>
<ItemDescription>Promoted from main by Ada Park</ItemDescription>
</ItemContent>
<ItemActions>
<ChevronRightIcon className="size-4 text-icon-muted" />
</ItemActions>
</Item>
);
}Header and footer
ItemHeader and ItemFooter span the item's full width.
Fix pricing table alignment on small screens
import { GitBranchIcon } from "lucide-react";
import {
Item,
ItemContent,
ItemDescription,
ItemFooter,
ItemHeader,
ItemTitle,
} from "@/components/ui/item";
import { Status } from "@/components/ui/status";
export default function ItemHeaderFooter() {
return (
<Item variant="outline" className="max-w-lg">
<ItemHeader>
<Status variant="success">Ready</Status>
<span className="text-xs text-faint">3 min ago</span>
</ItemHeader>
<ItemContent>
<ItemTitle>website-redesign</ItemTitle>
<ItemDescription>Fix pricing table alignment on small screens</ItemDescription>
</ItemContent>
<ItemFooter>
<span className="flex items-center gap-1.5 text-xs text-faint">
<GitBranchIcon className="size-3.5" />
feature/pricing
</span>
<span className="font-mono text-xs text-faint">a1b2c3d</span>
</ItemFooter>
</Item>
);
}Settings list
Give each control an aria-label, since the item's title doesn't label it.
Email and push alerts for mentions
Light, dark or match your system
English (United States)
100 Market Street, San Francisco, CA
import { BellIcon, CreditCardIcon, LanguagesIcon, PaletteIcon } from "lucide-react";
import { Button } from "@/components/ui/button";
import {
Item,
ItemActions,
ItemContent,
ItemDescription,
ItemGroup,
ItemMedia,
ItemTitle,
} from "@/components/ui/item";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { Switch } from "@/components/ui/switch";
const appearances = { light: "Light", dark: "Dark", system: "System" };
export default function ItemSettingsList() {
return (
<ItemGroup className="max-w-lg gap-0 overflow-hidden rounded-md border-[0.5px] border-border-muted *:data-[slot=item]:rounded-none [&>[data-slot=item]:not(:last-child)]:border-b-border-muted">
<Item size="sm">
<ItemMedia variant="icon">
<BellIcon />
</ItemMedia>
<ItemContent>
<ItemTitle>Notifications</ItemTitle>
<ItemDescription>Email and push alerts for mentions</ItemDescription>
</ItemContent>
<ItemActions>
<Switch defaultChecked aria-label="Notifications" />
</ItemActions>
</Item>
<Item size="sm">
<ItemMedia variant="icon">
<PaletteIcon />
</ItemMedia>
<ItemContent>
<ItemTitle>Appearance</ItemTitle>
<ItemDescription>Light, dark or match your system</ItemDescription>
</ItemContent>
<ItemActions>
<Select items={appearances} defaultValue="system">
<SelectTrigger size="sm" aria-label="Appearance">
<SelectValue />
</SelectTrigger>
<SelectContent>
{Object.entries(appearances).map(([value, label]) => (
<SelectItem key={value} value={value}>
{label}
</SelectItem>
))}
</SelectContent>
</Select>
</ItemActions>
</Item>
<Item size="sm">
<ItemMedia variant="icon">
<LanguagesIcon />
</ItemMedia>
<ItemContent>
<ItemTitle>Language</ItemTitle>
<ItemDescription>English (United States)</ItemDescription>
</ItemContent>
<ItemActions>
<Button variant="secondary" size="sm">
Change
</Button>
</ItemActions>
</Item>
<Item size="sm">
<ItemMedia variant="icon">
<CreditCardIcon />
</ItemMedia>
<ItemContent>
<ItemTitle>Billing address</ItemTitle>
<ItemDescription>100 Market Street, San Francisco, CA</ItemDescription>
</ItemContent>
<ItemActions>
<Button variant="secondary" size="sm">
Edit
</Button>
</ItemActions>
</Item>
</ItemGroup>
);
}With separators
ItemSeparator draws a line between items. Set the group's gap to zero when using it.
ada@example.com
sam@example.com
priya@example.com
jonah@example.com
import { PlusIcon } from "lucide-react";
import { Fragment } from "react";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { Button } from "@/components/ui/button";
import {
Item,
ItemActions,
ItemContent,
ItemDescription,
ItemGroup,
ItemMedia,
ItemSeparator,
ItemTitle,
} from "@/components/ui/item";
import { people } from "@/examples/data";
export default function ItemSeparators() {
return (
<ItemGroup className="max-w-lg gap-0">
{people.slice(0, 4).map((person, index) => (
<Fragment key={person.value}>
{index > 0 && <ItemSeparator className="my-0" />}
<Item size="sm">
<ItemMedia>
<Avatar>
<AvatarImage src={person.image} alt="" />
<AvatarFallback>{person.initials}</AvatarFallback>
</Avatar>
</ItemMedia>
<ItemContent>
<ItemTitle>{person.name}</ItemTitle>
<ItemDescription>{person.email}</ItemDescription>
</ItemContent>
<ItemActions>
<Button variant="ghost" size="icon-sm" aria-label={`Message ${person.name}`}>
<PlusIcon />
</Button>
</ItemActions>
</Item>
</Fragment>
))}
</ItemGroup>
);
}Props
| Part | Extends |
|---|---|
| ItemGroup | <div> |
| ItemSeparator | Separator |
| Item | <div> |
| ItemMedia | <div> |
| ItemContent | <div> |
| ItemTitle | <div> |
| ItemDescription | <p> |
| ItemActions | <div> |
| ItemHeader | <div> |
| ItemFooter | <div> |
The tables leave out className, style and render. Parts built on Base UI take them as its styling and composition guides describe.
ItemSeparator
| Prop | Type | Default |
|---|---|---|
orientationThe orientation of the separator. | "horizontal" | "vertical" | "horizontal" |
Item
| Prop | Type | Default |
|---|---|---|
variant | "default" | "outline" | "default" |
size | "default" | "sm" | "xs" | "default" |
ItemMedia
| Prop | Type | Default |
|---|---|---|
variant | "default" | "icon" | "image" | "default" |