Description List
Key and value rows for detail pages, with copy buttons and inline editing.
@drift-ui/description-listRegistry JSON- ID
- usr_8f2a
- Owner
- Status
- Active
- Created
- Oct 12, 2026, 09:41
On this page
Installation
The CLI also installs the npm packages and registry items it builds on. Register the namespace once first with https://drift-ui.dev/r/{style}/{name}.json. The CLI fills in {style} from your components.json and installs the Base UI or Radix build to match. See setup.
API reference
DescriptionList
A two column <dl> grid for DescriptionItem rows.
Other props go to the <dl>.
DescriptionItem
One label and value row. The value is the children, so editable rows take a string and the parent stores the edits.
| Prop | Type | Default |
|---|---|---|
labelRequiredReactNodeThe term. A string label also names the copy and edit buttons. | ReactNode | |
copyablebooleanAdds a button that copies the value text. | boolean | |
editablebooleanEdits the string children in place with InlineEdit. | boolean | |
onValueChange(value: string) => void | Promise<void>Called with the edited text. A pending promise keeps the field read-only and a rejection keeps it open. | (value: string) => void | Promise<void> | |
placeholderstringShown by an editable row while its value is empty. | string |
Other props go to the <div>.
Source
"use client"
import * as React from "react"
import { CheckIcon, CopyIcon } from "lucide-react"
import { useCopyToClipboard } from "@/hooks/use-copy-to-clipboard"
import { cn } from "@/lib/utils"
import { InlineEdit } from "@/components/ui/inline-edit"
function DescriptionList({ className, ...props }: React.ComponentProps<"dl">) {
return (
<dl
data-slot="description-list"
className={cn(
"grid grid-cols-[minmax(6rem,auto)_1fr] gap-x-6 text-sm",
className
)}
{...props}
/>
)
}
function DescriptionItem({
label,
copyable = false,
editable = false,
onValueChange,
placeholder,
children,
className,
...props
}: React.ComponentProps<"div"> & {
label: React.ReactNode
copyable?: boolean
editable?: boolean
onValueChange?: (value: string) => void | Promise<void>
placeholder?: string
}) {
const { copied, copy } = useCopyToClipboard()
const valueRef = React.useRef<HTMLDivElement>(null)
const labelText = typeof label === "string" ? label : undefined
return (
<div
data-slot="description-item"
className={cn(
"col-span-2 grid min-h-11 grid-cols-subgrid items-center border-b py-1.5 last:border-b-0",
className
)}
{...props}
>
<dt className="text-muted-foreground">{label}</dt>
<dd className="flex min-w-0 items-center gap-1">
<div ref={valueRef} className="min-w-0 flex-1 break-words">
{editable ? (
<InlineEdit
aria-label={labelText}
value={typeof children === "string" ? children : ""}
placeholder={placeholder}
onSave={onValueChange ?? (() => {})}
/>
) : (
children
)}
</div>
{copyable && (
<button
type="button"
data-slot="description-item-copy"
aria-label={labelText ? `Copy ${labelText}` : "Copy"}
onClick={() =>
copy(
typeof children === "string"
? children
: (valueRef.current?.textContent ?? "")
)
}
className="inline-flex size-7 shrink-0 items-center justify-center rounded-md text-muted-foreground outline-none hover:bg-accent hover:text-accent-foreground focus-visible:ring-[3px] focus-visible:ring-ring/50 [&_svg]:size-3.5"
>
{copied ? <CheckIcon /> : <CopyIcon />}
</button>
)}
{copyable && (
<span role="status" className="sr-only">
{copied ? "Copied" : ""}
</span>
)}
</dd>
</div>
)
}
export { DescriptionItem, DescriptionList }