Item
A versatile component for displaying content with media, title, description, and actions.
The Item component is a straightforward flex container that can house nearly any type of content. Use it to display a title, description, and actions. Group it with the ItemGroup component to create a list of items.
Installation
CLI
npx shadcn@latest add https://exawizards.com/exabase/design/registry/item.jsonManual
Copy and paste the following code into your project.
import * as React from "react"
import { mergeProps } from "@base-ui/react/merge-props"
import { useRender } from "@base-ui/react/use-render"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"
import { Separator } from "@/components/ui/separator"
function ItemGroup({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
role="list"
data-slot="item-group"
className={cn(
"group/item-group flex w-full flex-col gap-4 has-data-[size=sm]:gap-2.5 has-data-[size=xs]:gap-2",
className
)}
{...props}
/>
)
}
function ItemSeparator({
className,
...props
}: React.ComponentProps<typeof Separator>) {
return (
<Separator
data-slot="item-separator"
orientation="horizontal"
className={cn("my-0", className)}
{...props}
/>
)
}
const itemVariants = cva(
"group/item flex w-full flex-wrap items-center rounded-lg border text-sm transition-colors duration-100 outline-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 in-data-[slot=dropdown-menu-content]:p-0 in-data-[slot=select-content]:p-0 in-data-[slot=select-trigger]:p-0 [a]:transition-colors [a]:hover:bg-muted",
{
variants: {
variant: {
default: "border-transparent",
outline: "border-border",
muted: "border-transparent bg-muted/50",
},
size: {
default: "gap-2.5 px-3 py-2.5",
sm: "gap-2.5 px-3 py-2.5",
xs: "gap-2 px-2.5 py-2",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
}
)
function Item({
className,
variant = "default",
size = "default",
render,
...props
}: useRender.ComponentProps<"div"> & VariantProps<typeof itemVariants>) {
return useRender({
defaultTagName: "div",
props: mergeProps<"div">(
{
className: cn(itemVariants({ variant, size, className })),
},
props
),
render,
state: {
slot: "item",
variant,
size,
},
})
}
const itemMediaVariants = cva(
"flex shrink-0 items-center justify-center gap-2 group-has-data-[slot=item-description]/item:translate-y-0.5 group-has-data-[slot=item-description]/item:self-start [&_svg]:pointer-events-none",
{
variants: {
variant: {
default: "bg-transparent",
icon: "[&_svg:not([class*='size-'])]:size-4",
image:
"size-10 overflow-hidden rounded-sm group-data-[size=sm]/item:size-8 group-data-[size=xs]/item:size-6 [&_img]:size-full [&_img]:object-cover",
},
},
defaultVariants: {
variant: "default",
},
}
)
function ItemMedia({
className,
variant = "default",
...props
}: React.ComponentProps<"div"> & VariantProps<typeof itemMediaVariants>) {
return (
<div
data-slot="item-media"
data-variant={variant}
className={cn(itemMediaVariants({ variant, className }))}
{...props}
/>
)
}
function ItemContent({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="item-content"
className={cn(
"flex flex-1 flex-col gap-1 group-data-[size=xs]/item:gap-0 [&+[data-slot=item-content]]:flex-none",
className
)}
{...props}
/>
)
}
function ItemTitle({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="item-title"
className={cn(
"line-clamp-1 flex w-fit items-center gap-2 text-sm leading-snug font-medium underline-offset-4",
className
)}
{...props}
/>
)
}
function ItemDescription({ className, ...props }: React.ComponentProps<"p">) {
return (
<p
data-slot="item-description"
className={cn(
"line-clamp-2 text-left text-sm leading-normal font-normal text-muted-foreground group-data-[size=xs]/item:text-xs [&>a]:underline [&>a]:underline-offset-4 [&>a:hover]:text-primary",
className
)}
{...props}
/>
)
}
function ItemActions({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="item-actions"
className={cn("flex items-center gap-2", className)}
{...props}
/>
)
}
function ItemHeader({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="item-header"
className={cn(
"flex basis-full items-center justify-between gap-2",
className
)}
{...props}
/>
)
}
function ItemFooter({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="item-footer"
className={cn(
"flex basis-full items-center justify-between gap-2",
className
)}
{...props}
/>
)
}
export {
Item,
ItemMedia,
ItemContent,
ItemActions,
ItemGroup,
ItemSeparator,
ItemTitle,
ItemDescription,
ItemHeader,
ItemFooter,
}
Anatomy
- Item: The main component for displaying content with media, title, description, and actions.
- ItemContent: Wraps the title and description of the item.
- ItemTitle: Displays the title of the item.
- ItemDescription: Displays the description of the item.
- ItemMedia: Displays media content such as icons, images, or avatars.
- ItemActions: Container for action buttons or other interactive elements.
- ItemHeader: Displays a header above the item content.
- ItemFooter: Displays a footer below the item content.
- ItemContent: Wraps the title and description of the item.
- ItemGroup: A container that groups related items together with consistent styling.
- ItemSeparator: A separator between items in a group.
Usage
import {
Item,
ItemActions,
ItemContent,
ItemDescription,
ItemMedia,
ItemTitle,
} from "@/components/ui/item"<Item>
<ItemMedia variant="icon">
<Icon />
</ItemMedia>
<ItemContent>
<ItemTitle>Title</ItemTitle>
<ItemDescription>Description</ItemDescription>
</ItemContent>
<ItemActions>
<Button>Action</Button>
</ItemActions>
</Item>Item vs Field
Use Field if you need to display a form input such as a checkbox, input, radio, or select.
If you only need to display content such as a title, description, and actions, use Item.
Variant
Use the variant prop to change the visual style of the item.
Size
Use the size prop to change the size of the item. Available sizes are default, sm, and xs.
Examples
Icon
Use ItemMedia with variant="icon" to display an icon.
Avatar
Use ItemMedia to display an avatar.
Image
Use ItemMedia with variant="image" to display an image.
Group
Use ItemGroup to group related items together.
Header
Use ItemHeader to add a header above the item content.
Link
Use the render prop to render the item as a link. The hover and focus states will be applied to the anchor element.
Dropdown
Use Item inside a DropdownMenu for rich dropdown content.
API
Item
The main component for displaying content with media, title, description, and actions.
| Prop | Type | Default | Description |
|---|---|---|---|
variant | "default" | "outline" | "muted" | "default" | The visual style of the item. |
size | "default" | "sm" | "xs" | "default" | The size of the item. |
render | ReactNode | undefined | Render a custom component as the item. |
ItemMedia
| Prop | Type | Default | Description |
|---|---|---|---|
variant | "default" | "icon" | "image" | "default" | The type of media to display. |