Shadcn Item for React and Tailwind
A versatile component for displaying content.
A simple item with title and description.
Installation
bunx --bun shadcn@latest add https://kit.dev/r/item.jsonpnpm dlx shadcn@latest add https://kit.dev/r/item.jsonnpx shadcn@latest add https://kit.dev/r/item.jsonyarn shadcn@latest add https://kit.dev/r/item.json<Step>This component depends on Separator. Install it first if you haven't already.</Step>
Install the following dependencies:
bun add @ark-ui/react tailwind-variantspnpm add @ark-ui/react tailwind-variantsnpm install @ark-ui/react tailwind-variantsyarn add @ark-ui/react tailwind-variantsCopy and paste the following code into your project.
"use client";
import { ark } from "@ark-ui/react/factory";
import type React from "react";
import { tv, type VariantProps } from "tailwind-variants";
import { cn } from "@/lib/utils";
import { Separator } from "@/components/ui/separator";
export const ItemGroup = (props: React.ComponentProps<typeof ark.div>) => {
const { className, ...rest } = props;
return (
<ark.div
className={cn(
"group/item-group",
"flex w-full flex-col gap-4",
className
)}
data-slot="item-group"
role="list"
{...rest}
/>
);
};
export const ItemSeparator = (
props: React.ComponentProps<typeof Separator>
) => {
const { className, ...rest } = props;
return (
<Separator
className={cn("my-2", className)}
data-slot="item-separator"
orientation="horizontal"
{...rest}
/>
);
};
const itemVariants = tv({
base: [
"[--space:--spacing(3)]",
"group/item",
"flex w-full flex-wrap items-center",
"gap-(--space) p-(--space)",
"in-data-[slot=menu-content]:p-0",
"text-sm",
"rounded-xl border",
"transition-colors duration-100",
"[a]:transition-colors [a]:hover:bg-muted",
"outline-none focus-visible:border-primary focus-visible:ring-[3px] focus-visible:ring-ring/32",
"[&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0",
],
variants: {
variant: {
default: "border-transparent",
outline: "border-border shadow-xs/5",
muted: "border-transparent bg-muted/48 shadow-muted/5 shadow-xs",
},
},
defaultVariants: {
variant: "default",
},
});
interface ItemProps
extends React.ComponentProps<typeof ark.div>,
VariantProps<typeof itemVariants> {}
export const Item = (props: ItemProps) => {
const { variant = "default", className, ...rest } = props;
return (
<ark.div
className={cn(itemVariants({ variant }), className)}
data-slot="item"
data-variant={variant}
{...rest}
/>
);
};
const itemMediaVariants = tv({
base: [
"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",
"rounded-xl",
"overflow-hidden",
"[&_img]:size-full [&_img]:object-cover",
],
},
},
defaultVariants: {
variant: "default",
},
});
interface ItemMediaProps
extends React.ComponentProps<typeof ark.div>,
VariantProps<typeof itemMediaVariants> {}
export const ItemMedia = (props: ItemMediaProps) => {
const { variant = "default", className, ...rest } = props;
return (
<ark.div
className={cn(itemMediaVariants({ variant, className }))}
data-slot="item-media"
data-variant={variant}
{...rest}
/>
);
};
export const ItemContent = (props: React.ComponentProps<typeof ark.div>) => {
const { className, ...rest } = props;
return (
<ark.div
className={cn(
"flex flex-1 flex-col gap-0.5",
"[&+[data-slot=item-content]]:flex-none",
className
)}
data-slot="item-content"
{...rest}
/>
);
};
export const ItemTitle = (props: React.ComponentProps<typeof ark.div>) => {
const { className, ...rest } = props;
return (
<ark.div
className={cn(
"w-fit",
"flex items-center gap-2",
"line-clamp-1 font-medium text-sm leading-snug",
"underline-offset-4",
className
)}
data-slot="item-title"
{...rest}
/>
);
};
export const ItemDescription = (props: React.ComponentProps<typeof ark.p>) => {
const { className, ...rest } = props;
return (
<ark.p
className={cn(
"line-clamp-2 text-left font-normal text-muted-foreground text-sm leading-normal",
"[&>a:hover]:text-primary",
"[&>a]:underline [&>a]:underline-offset-4",
className
)}
data-slot="item-description"
{...rest}
/>
);
};
export const ItemActions = (props: React.ComponentProps<typeof ark.div>) => {
const { className, ...rest } = props;
return (
<ark.div
className={cn("flex items-center gap-2", className)}
data-slot="item-actions"
{...rest}
/>
);
};
export const ItemHeader = (props: React.ComponentProps<typeof ark.div>) => {
const { className, ...rest } = props;
return (
<ark.div
className={cn(
"flex basis-full items-center justify-between gap-2",
"[&_img]:size-full [&_img]:rounded-xl [&_img]:object-cover",
className
)}
data-slot="item-header"
{...rest}
/>
);
};
export const ItemFooter = (props: React.ComponentProps<typeof ark.div>) => {
const { className, ...rest } = props;
return (
<ark.div
className={cn(
"flex basis-full items-center justify-between gap-2",
className
)}
data-slot="item-footer"
{...rest}
/>
);
};Update the import paths to match your project setup.
Anatomy
ItemGroup
├── ItemSeparator
└── Item
├── ItemMedia
├── ItemHeader
├── ItemContent
│ ├── ItemTitle
│ └── ItemDescription
├── ItemActions
└── ItemFooterUsage
import {
Item,
ItemActions,
ItemContent,
ItemDescription,
ItemMedia,
ItemTitle,
} from "@/components/ui/item";<Item>
<ItemMedia />
<ItemContent>
<ItemTitle />
<ItemDescription />
</ItemContent>
<ItemActions />
</Item>Variant
Default
Outline
Muted
Custom spacing
The [--space:--spacing("value")] on ItemContent controls the internal spacing.
Default spacing is --spacing(3).
You can use breakpoint utilities to change the internal spacing at different screen sizes.
md:[--space:--spacing(6)] lg:[--space:--spacing(8)]Examples
Icon
Avatar
Image
Group
Use ItemGroup to group related items together.
Header
Use ItemHeader to add a header above the item content.
Link
Use the asChild 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 menu or dropdown to display rich content in menu items.
API Reference
Item
Layout for list items with media, title, description, and actions. Use [--space:--spacing("value")] via className to control padding and gap. Supports asChild.
| Prop | Type | Default |
|---|---|---|
variant | "default" | "outline" | "muted" | "default" |
asChild | boolean | false |
className | string | - |
| Attribute | Default |
|---|---|
--space | --spacing(3) |
ItemGroup
Groups related items with consistent spacing. Uses a fixed gap; adjust layout with className if needed. Has role="list" for accessibility.
| Prop | Type | Default |
|---|---|---|
className | string | - |
asChild | boolean | false |
ItemSeparator
Horizontal divider between items in a group.
| Prop | Type | Default |
|---|---|---|
className | string | - |
asChild | boolean | false |
ItemMedia
Avatar, icon, or image for the item. Supports icon and image variants.
| Prop | Type | Default |
|---|---|---|
variant | "default" | "icon" | "image" | "default" |
className | string | - |
asChild | boolean | false |
ItemContent
Wraps the item title and description text.
| Prop | Type | Default |
|---|---|---|
className | string | - |
asChild | boolean | false |
ItemTitle
Primary title or heading for the item.
| Prop | Type | Default |
|---|---|---|
className | string | - |
asChild | boolean | false |
ItemDescription
Secondary description or subtitle for the item.
| Prop | Type | Default |
|---|---|---|
className | string | - |
asChild | boolean | false |
ItemActions
Holds action buttons or controls.
| Prop | Type | Default |
|---|---|---|
className | string | - |
asChild | boolean | false |
ItemHeader
Full-width header row above the item content.
| Prop | Type | Default |
|---|---|---|
className | string | - |
asChild | boolean | false |
ItemFooter
Full-width footer row below the item content.
| Prop | Type | Default |
|---|---|---|
className | string | - |
asChild | boolean | false |