Primitives
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
Grouped content rows
- <ItemGroup>
- <Item>
- <ItemHeader />
- <ItemMedia />
- <ItemContent>
- <ItemTitle />
- <ItemDescription />
- <ItemActions />
- <ItemFooter />
- <ItemSeparator />
- <Item>
Installation
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.
npx shadcn@latest add https://control-ui.dev/r/item.jsonDependencies
Raw code
Primary installed source
"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"> & { render?: RenderProp<ComponentProps<"div">>; style?: CSSProperties & ItemKnobStyle;};export type ItemGroupProps = ComponentProps<"div"> & { style?: CSSProperties & ItemKnobStyle };export type ItemSeparatorProps = ComponentProps<"div">;export type ItemActionsProps = Omit<ComponentProps<"div">, "style"> & { showOnHover?: boolean; style?: CSSProperties & ItemKnobStyle;};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 flex-wrap items-center [&[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", className)} {...props} /> );}export function ItemTitle({ render, className, ...props }: ItemTitleProps) { return useRender({ defaultTagName: "div", render, props: { ...props, "data-control-ui": "item", "data-control-family": "item", "data-slot": "title", className: cn("flex w-fit items-center", className), }, });}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({ showOnHover, className, ...props }: ItemActionsProps) { return ( <div data-control-ui="item" data-control-family="item" data-slot="actions" data-show-on-hover={showOnHover || undefined} className={cn("flex shrink-0 items-center", 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", 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", className)} {...props} /> );}Knobs
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-* · 8 knobsHow the cascade resolves--cui-item-radius<length-percentage>var(--radius-lg)--cui-item-hover-background<color>transparent--cui-item-hover-border-color<color>var(--_item-border-color)--cui-item-border-width<length>0px--cui-item-active-scale<number>0.99--cui-item-gap<length-percentage>calc(var(--spacing) * 3)--cui-item-padding-block<length-percentage>calc(var(--spacing) * 3)--cui-item-padding-inline<length-percentage>calc(var(--spacing) * 3)