Item
BetaBeta — the props contract is close to final, but small breaking changes can still land.List row with media, content, and trailing actions.
Composition
The preferred shape for composing the installed primitive from its exported parts.
Grouped content rows
ItemGroup owns list grouping while ItemSeparator divides independently composed rows.
ItemGroup ├── Item │ ├── ItemMedia │ ├── ItemContent │ └── ItemActions ├── ItemSeparator └── Item
First install and activate one skin. Core deliberately contains no visual token defaults.
The Control UI source installs this primitive from src/registry/sources/control-ui/ui/item.tsx. Install it on its own with the command above, or inspect the source below.
npx shadcn@latest add https://control-ui.dev/r/item.jsonKnobs
Typed custom properties the recipe paints with. Set one on the root — style, a utility class, or a skin — and every slot inherits it.
--cui-item-* · 5 knobsHow the cascade resolves--cui-item-radius<length-percentage>var(--radius-lg)--cui-item-hover-background*transparent--cui-item-hover-border-color<color>var(--_item-border-color)--cui-item-border-width<length>0px--cui-item-active-scale<number>1--cui-separator-* · 1 knobsHow the cascade resolves--cui-separator-root-background<color>var(--border)Installed dependencies
Support files installed with this primitive — shared ones arrive once with your first Control UI component. Public dependencies stay linked to their own pages.
src/registry/sources/control-ui/ui/separator.tsxSkinRaw code
This primitive's source and the support files it installs with
"use client";import { useRender } from "@base-ui/react/use-render";import type { ComponentProps, CSSProperties } from "react";import type { RenderProp } from "@/components/control-ui/control-props";import type { ItemKnobStyle } from "@/components/control-ui/knob-contracts/item-knobs";import { cn } from "@/components/control-ui/lib/cn";import { Separator } from "@/components/control-ui/ui/separator";export const itemVariants = ["default", "outline", "muted"] as const;export type ItemVariant = (typeof itemVariants)[number];export type ItemProps = Omit< ComponentProps<"div"> & { variant?: ItemVariant; render?: RenderProp<ComponentProps<"div">>; }, "style"> & { style?: CSSProperties & ItemKnobStyle };export type ItemDescriptionProps = Omit<ComponentProps<"p">, "style"> & { style?: CSSProperties & ItemKnobStyle };export type ItemFooterProps = Omit<ComponentProps<"div">, "style"> & { style?: CSSProperties & ItemKnobStyle };export type ItemMediaProps = Omit<ComponentProps<"div">, "style"> & { style?: CSSProperties & ItemKnobStyle };export type ItemTitleProps = Omit<ComponentProps<"div">, "style"> & { style?: CSSProperties & ItemKnobStyle };export type ItemGroupProps = ComponentProps<"div"> & { style?: CSSProperties & ItemKnobStyle };export type ItemSeparatorProps = ComponentProps<"div">;export function ItemGroup({ className, ...props }: ItemGroupProps) { return ( // biome-ignore lint/a11y/useSemanticElements: Item may render as a link or button, so a ul would require invalid wrappers. <div role="list" data-control-ui="item" data-control-family="item" data-slot="group" className={cn("group/item-group flex flex-col", className)} {...props} /> );}export function ItemSeparator({ className, ...props }: ItemSeparatorProps) { return <Separator data-control-ui="item" data-slot="separator" orientation="horizontal" className={cn("my-0", className)} {...props} />;}export function Item({ variant = "default", render, className, children, ...props }: ItemProps) { return useRender({ defaultTagName: "div", render, props: { ...props, "data-control-ui": "item", "data-control-family": "item", "data-slot": "root", "data-variant": variant, className: cn("flex items-center gap-3 p-3 [&[href]]:cursor-pointer", className), children, }, });}export function ItemMedia({ className, ...props }: ItemMediaProps) { return ( <div data-control-ui="item" data-control-family="item" data-slot="media" className={cn("flex shrink-0 items-center justify-center self-start [&>svg]:size-5 [&>svg]:shrink-0", className)} {...props} /> );}export function ItemContent({ className, ...props }: ComponentProps<"div"> & { style?: CSSProperties & ItemKnobStyle }) { return ( <div data-control-ui="item" data-control-family="item" data-slot="content" className={cn("flex min-w-0 flex-1 flex-col gap-0.5", className)} {...props} /> );}export function ItemTitle({ className, ...props }: ItemTitleProps) { return ( <div data-control-ui="item" data-control-family="item" data-slot="title" className={cn("flex w-fit items-center gap-2", className)} {...props} /> );}export function ItemDescription({ className, ...props }: ItemDescriptionProps) { return ( <p data-control-ui="item" data-control-family="item" data-slot="description" className={cn("line-clamp-2", className)} {...props} /> );}// never shrinks, so long content column cannot squeeze actionsexport function ItemActions({ className, ...props }: ComponentProps<"div"> & { style?: CSSProperties & ItemKnobStyle }) { return ( <div data-control-ui="item" data-control-family="item" data-slot="actions" className={cn("flex shrink-0 items-center gap-1.5", className)} {...props} /> );}export function ItemHeader({ className, ...props }: ComponentProps<"div"> & { style?: CSSProperties & ItemKnobStyle }) { return ( <div data-control-ui="item" data-control-family="item" data-slot="header" className={cn("flex basis-full items-center justify-between gap-2", className)} {...props} /> );}export function ItemFooter({ className, ...props }: ItemFooterProps) { return ( <div data-control-ui="item" data-control-family="item" data-slot="footer" className={cn("flex basis-full items-center justify-between gap-2", className)} {...props} /> );}