Components / Display
Description List Term and value pairs for detail pages, settings summaries and record sidebars. A semantic dl with a side-by-side layout that stacks on small screens, or a stacked layout, plus optional dividers and two sizes. Works in server components.
Customer Harbor & Pine Studio Invoice INV-2041 Issued October 1, 2026 Due October 31, 2026 · Net 30 Amount $4,280.00
import {
DescriptionDetails,
DescriptionList,
DescriptionTerm,
} from "@/components/ui/description-list" ;
export default function DescriptionListDemo () {
return (
< DescriptionList divided className = "w-full max-w-lg" >
< DescriptionTerm >Customer</ DescriptionTerm >
< DescriptionDetails >Harbor & Pine Studio</ DescriptionDetails >
< DescriptionTerm >Invoice</ DescriptionTerm >
< DescriptionDetails className = "font-mono text-[0.8125rem]" >
INV-2041
</ DescriptionDetails >
< DescriptionTerm >Issued</ DescriptionTerm >
< DescriptionDetails >October 1, 2026</ DescriptionDetails >
< DescriptionTerm >Due</ DescriptionTerm >
< DescriptionDetails >October 31, 2026 · Net 30</ DescriptionDetails >
< DescriptionTerm >Amount</ DescriptionTerm >
< DescriptionDetails className = "font-medium tabular-nums" >
$4,280.00
</ DescriptionDetails >
</ DescriptionList >
);
}
CLI Manual
$ pnpm dlx shadcn@latest add @desyne/description-list
The CLI installs dependencies and any other components this one uses.
import {
DescriptionDetails,
DescriptionList,
DescriptionTerm,
} from "@/components/ui/description-list" ;
< DescriptionList divided >
< DescriptionTerm >Customer</ DescriptionTerm >
< DescriptionDetails >Harbor & Pine Studio</ DescriptionDetails >
< DescriptionTerm >Amount</ DescriptionTerm >
< DescriptionDetails >$4,280.00</ DescriptionDetails >
</ DescriptionList >
Terms and details are direct children
The layout is a CSS grid on the <dl> that styles its direct <dt> and
<dd> children, so put them straight inside DescriptionList, one
DescriptionDetails per term. Use a Fragment (not a <div>) when mapping
over pairs.
Description List: read-only fields of one record, such as an invoice, a deployment or a user profile.
Table : many records that share the same fields.
Item : a list of entities with media and actions.
Card : a container for a description list with a title.
< DescriptionList > { /* <dl>, layout, divided, size */ }
< DescriptionTerm /> { /* <dt> */ }
< DescriptionDetails /> { /* <dd> */ }
</ DescriptionList >
Part Renders Notes DescriptionList<dl>Grid. horizontal: term column (minmax(7rem, 33%)) and details column from sm up, stacked below. stacked: one column. DescriptionTerm<dt>Muted label. DescriptionDetails<dd>Foreground value. Long words wrap anywhere instead of overflowing.
layout="stacked" puts each term above its details, for narrow sidebars and multi-line values like addresses.
Shipping address Rosa Delgado 218 Kent Avenue, Apt 4B Brooklyn, NY 11249 Delivery method Express · 1–2 business days Payment Mastercard ending 8812
import {
DescriptionDetails,
DescriptionList,
DescriptionTerm,
} from "@/components/ui/description-list" ;
export default function DescriptionListStacked () {
return (
< DescriptionList layout = "stacked" className = "w-full max-w-xs" >
< DescriptionTerm >Shipping address</ DescriptionTerm >
< DescriptionDetails >
Rosa Delgado
< br />
218 Kent Avenue, Apt 4B
< br />
Brooklyn, NY 11249
</ DescriptionDetails >
< DescriptionTerm >Delivery method</ DescriptionTerm >
< DescriptionDetails >Express · 1–2 business days</ DescriptionDetails >
< DescriptionTerm >Payment</ DescriptionTerm >
< DescriptionDetails >Mastercard ending 8812</ DescriptionDetails >
</ DescriptionList >
);
}
Details are plain elements, so they can hold badges, links and buttons. Make DescriptionDetails a flex row to push an action to the end, and give each button a name that includes the field.
Full name Tomás Herrera Edit Email tomas@harborpine.co Edit Role Admin Edit Time zone Europe/Madrid (UTC+2) Edit
import { Fragment } from "react" ;
import { Badge } from "@/components/ui/badge" ;
import { Button } from "@/components/ui/button" ;
import {
DescriptionDetails,
DescriptionList,
DescriptionTerm,
} from "@/components/ui/description-list" ;
const rows = [
{ term: "Full name" , value: "Tomás Herrera" },
{ term: "Email" , value: "tomas@harborpine.co" },
{
term: "Role" ,
value : (
< Badge size = "sm" variant = "soft" >
Admin
</ Badge >
),
},
{ term: "Time zone" , value: "Europe/Madrid (UTC+2)" },
];
export default function DescriptionListActions () {
return (
< DescriptionList divided className = "w-full max-w-lg" >
{rows. map (( r ) => (
< Fragment key = {r.term}>
< DescriptionTerm className = "flex items-center" >
{r.term}
</ DescriptionTerm >
< DescriptionDetails className = "flex items-center justify-between gap-4" >
< span className = "min-w-0" >{r.value}</ span >
< Button
variant = "link"
size = "sm"
aria-label = { `Edit ${ r . term . toLowerCase () }` }
>
Edit
</ Button >
</ DescriptionDetails >
</ Fragment >
))}
</ DescriptionList >
);
}
size="sm" uses 13px text and tighter rows for cards and side panels; md is the default.
Region eu-west-1 Runtime Node.js 22 Memory 1024 MB Region eu-west-1 Runtime Node.js 22 Memory 1024 MB
import {
DescriptionDetails,
DescriptionList,
DescriptionTerm,
} from "@/components/ui/description-list" ;
export default function DescriptionListSizes () {
return (
< div className = "grid w-full max-w-2xl gap-8 sm:grid-cols-2" >
< DescriptionList size = "sm" divided >
< DescriptionTerm >Region</ DescriptionTerm >
< DescriptionDetails >eu-west-1</ DescriptionDetails >
< DescriptionTerm >Runtime</ DescriptionTerm >
< DescriptionDetails >Node.js 22</ DescriptionDetails >
< DescriptionTerm >Memory</ DescriptionTerm >
< DescriptionDetails >1024 MB</ DescriptionDetails >
</ DescriptionList >
< DescriptionList size = "md" divided >
< DescriptionTerm >Region</ DescriptionTerm >
< DescriptionDetails >eu-west-1</ DescriptionDetails >
< DescriptionTerm >Runtime</ DescriptionTerm >
< DescriptionDetails >Node.js 22</ DescriptionDetails >
< DescriptionTerm >Memory</ DescriptionTerm >
< DescriptionDetails >1024 MB</ DescriptionDetails >
</ DescriptionList >
</ div >
);
}
A deployment summary in a Card , with a status badge, a commit hash and a copyable domain.
Status Ready Commit a41c9e2Fix timezone offset in invoice dates Domain app.harborpine.co Build time 48s Created Oct 4, 2026 at 10:12 AM
import { CopyIcon } from "lucide-react" ;
import { Badge } from "@/components/ui/badge" ;
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card" ;
import {
DescriptionDetails,
DescriptionList,
DescriptionTerm,
} from "@/components/ui/description-list" ;
export default function DescriptionListRecipeDetailCard () {
return (
< Card className = "w-full max-w-lg" >
< CardHeader separator >
< CardTitle >Deployment dpl_8fK2a</ CardTitle >
< CardDescription >
Production · triggered by a push to main
</ CardDescription >
</ CardHeader >
< CardContent >
< DescriptionList divided size = "sm" >
< DescriptionTerm >Status</ DescriptionTerm >
< DescriptionDetails >
< Badge size = "sm" variant = "dot" color = "success" >
Ready
</ Badge >
</ DescriptionDetails >
< DescriptionTerm >Commit</ DescriptionTerm >
< DescriptionDetails className = "flex items-center gap-1.5" >
< code className = "font-mono text-xs" >a41c9e2</ code >
< span className = "truncate text-muted-foreground" >
Fix timezone offset in invoice dates
</ span >
</ DescriptionDetails >
< DescriptionTerm >Domain</ DescriptionTerm >
< DescriptionDetails className = "flex items-center gap-1.5" >
app.harborpine.co
< CopyIcon aria-hidden className = "size-3.5 text-muted-foreground" />
</ DescriptionDetails >
< DescriptionTerm >Build time</ DescriptionTerm >
< DescriptionDetails className = "tabular-nums" >48s</ DescriptionDetails >
< DescriptionTerm >Created</ DescriptionTerm >
< DescriptionDetails >Oct 4, 2026 at 10:12 AM</ DescriptionDetails >
</ DescriptionList >
</ CardContent >
</ Card >
);
}
Uses native <dl>, <dt> and <dd>, so screen readers announce terms and their values as pairs.
Dividers are borders, not separate elements, so they add nothing to the reading order.
When a value is only an icon or a color (e.g. a status dot), include text, as the Badge does.
Give actions in details names that include the field, e.g. aria-label="Edit email", not just "Edit".
Attribute On data-slot="description-list"DescriptionListdata-layoutDescriptionList (horizontal · stacked)data-slot="description-term"DescriptionTermdata-slot="description-details"DescriptionDetails
The horizontal layout reads --dl-term-width (default 33%) for the term column:
< DescriptionList className = "[--dl-term-width:12rem]" >…</ DescriptionList >
The tailwind-variants function behind DescriptionList, for applying the layout to your own <dl>.
Also accepts every prop of <dl>.
No extra props. Accept every prop of <dt> and <dd>.
Card : frame a description list with a title.
Table : many records with the same fields.
Badge : statuses inside details.
PreviousTimeline Next Empty