Item
Flexible row layout with leading media, content, and trailing actions. Install Item from the VLLNT UI registry with the shadcn CLI.
Flexible row layout with leading media, content, and trailing actions. Part of the form family in VLLNT UI, it ships as a machine-readable registry entry — copy the source directly into your app with the shadcn CLI and own it, no runtime dependency on a component library.
Preview
Switch between light and dark to inspect the embedded Storybook preview.
Installation
Add Item to your project with the shadcn CLI. The source lands in your codebase, ready to adapt:
pnpm dlx shadcn@latest add https://ui.vllnt.ai/r/item.jsonSource
import * as React from "react";
import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "../../lib/utils";
const itemVariants = cva("flex items-center gap-3 rounded-md text-sm", {
defaultVariants: {
size: "default",
variant: "default",
},
variants: {
size: {
default: "p-3",
sm: "p-2",
},
variant: {
default: "",
muted: "bg-muted/50",
outline: "border border-border",
},
},
});
/** Flexible row with leading media, content, and trailing actions. */
export type ItemProps = React.ComponentPropsWithoutRef<"div"> &
VariantProps<typeof itemVariants>;
const Item = ({
className,
ref,
size,
variant,
...props
}: ItemProps & { ref?: React.Ref<HTMLDivElement> }) => (
<div
className={cn(itemVariants({ size, variant }), className)}
data-slot="item"
ref={ref}
{...props}
/>
);
Item.displayName = "Item";
/** Leading slot for an icon, avatar, or thumbnail. */
export type ItemMediaProps = React.ComponentPropsWithoutRef<"div">;
const ItemMedia = ({
className,
ref,
...props
}: ItemMediaProps & { ref?: React.Ref<HTMLDivElement> }) => (
<div
className={cn(
"flex shrink-0 items-center justify-center [&_svg]:size-5",
className,
)}
data-slot="item-media"
ref={ref}
{...props}
/>
);
ItemMedia.displayName = "ItemMedia";
/** Stacked title and description in the middle of an Item. */
export type ItemContentProps = React.ComponentPropsWithoutRef<"div">;
const ItemContent = ({
className,
ref,
...props
}: ItemContentProps & { ref?: React.Ref<HTMLDivElement> }) => (
<div
className={cn("flex min-w-0 flex-1 flex-col gap-0.5", className)}
data-slot="item-content"
ref={ref}
{...props}
/>
);
ItemContent.displayName = "ItemContent";
/** Primary line of text inside an ItemContent. */
export type ItemTitleProps = React.ComponentPropsWithoutRef<"div">;
const ItemTitle = ({
className,
ref,
...props
}: ItemTitleProps & { ref?: React.Ref<HTMLDivElement> }) => (
<div
className={cn("font-medium leading-none", className)}
data-slot="item-title"
ref={ref}
{...props}
/>
);
ItemTitle.displayName = "ItemTitle";
/** Secondary muted line of text inside an ItemContent. */
export type ItemDescriptionProps = React.ComponentPropsWithoutRef<"p">;
const ItemDescription = ({
className,
ref,
...props
}: ItemDescriptionProps & { ref?: React.Ref<HTMLParagraphElement> }) => (
<p
className={cn("text-sm text-muted-foreground", className)}
data-slot="item-description"
ref={ref}
{...props}
/>
);
ItemDescription.displayName = "ItemDescription";
/** Trailing slot for actions such as buttons or switches. */
export type ItemActionsProps = React.ComponentPropsWithoutRef<"div">;
const ItemActions = ({
className,
ref,
...props
}: ItemActionsProps & { ref?: React.Ref<HTMLDivElement> }) => (
<div
className={cn("flex shrink-0 items-center gap-2", className)}
data-slot="item-actions"
ref={ref}
{...props}
/>
);
ItemActions.displayName = "ItemActions";
export {
Item,
ItemActions,
ItemContent,
ItemDescription,
ItemMedia,
ItemTitle,
itemVariants,
};
Stories
Explore every variant and state in the interactive Storybook:
Preview
Switch between light and dark to inspect the embedded Storybook preview.