# Description List

> A React description list for detail pages: key-value grids for clients, invoices and profiles, with copy buttons, skeletons and iOS rows.

- Type: React component (Data Display)
- Access: Pro (the source needs a Wingo UI Pro plan, see https://wingo-ui.com/pricing)
- Version: 1.0.0
- Install: `npx wingo-ui@latest add description-list`
- Docs: https://wingo-ui.com/components/description-list

## Interaction

Read the details in document order: label above value on phones and in narrow cards, one to three columns once the container is wide enough (it follows its container, not the window). Hover a row with a mouse or Tab into it and a copy button appears next to a copyable value; click it or press Enter and the icon turns into a check while the tooltip says Copied. On a phone the copy buttons are always visible and have a 44px hit area. Tab only stops on real controls (copy buttons, actions, inline edits). While loading, values are skeleton bars that crossfade into the real text. The grouped variant reads like iOS settings: label left, value right, long values move under the label on narrow screens.

## Installation

```bash
npx wingo-ui@latest add description-list
```

Pro item: run `npx wingo-ui@latest login` once with a Pro account. The CLI adds the files, their dependencies and a version record for updates.

With shadcn: `npx shadcn@latest add @wingo-ui/description-list` (needs the @wingo-ui registry with your API key in components.json)

Files:

- `components/ui/description-list.tsx`

npm dependencies: `npm i motion`

Wingo UI dependencies (installed automatically):

- [Copy Button](https://wingo-ui.com/components/copy-button) ([Markdown](https://wingo-ui.com/components/copy-button.md))
- [Skeleton](https://wingo-ui.com/components/skeleton) ([Markdown](https://wingo-ui.com/components/skeleton.md))
- [Motion presets](https://wingo-ui.com/components/motion) ([Markdown](https://wingo-ui.com/components/motion.md))
- [UIProvider](https://wingo-ui.com/components/ui-config) ([Markdown](https://wingo-ui.com/components/ui-config.md))

## Usage

```tsx
import { Pencil } from "lucide-react"
import { Button } from "@/components/ui/button"
import { DescriptionList, type DescriptionListItem } from "@/components/ui/description-list"

const CLIENT: DescriptionListItem[] = [
  { label: "Company", value: "Tech Solutions Inc." },
  { label: "Tax ID", value: "12-3456789", copy: true, mono: true },
  { label: "Registration no.", value: "DE-5512345", mono: true },
  { label: "Address", value: "233 S Wacker Dr, Chicago, IL 60606", span: 2 },
  { label: "IBAN", value: "GB29 NWBK 6016 1331 9268 19", copy: "GB29NWBK60161331926819", mono: true },
  { label: "Balance", value: "$12,480.00" },
]

export function ClientDetails({ loading }: { loading: boolean }) {
  return (
    <DescriptionList
      title="Client details"
      description="Billing details"
      actions={<Button variant="soft" size="sm" leftIcon={<Pencil />}>Edit</Button>}
      items={CLIENT}
      loading={loading}
    />
  )
}
```

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `items?` | `DescriptionListItem[]` |  | { id?, label, value, description?, icon?, copy?: boolean \| string, span?: 1 \| 2 \| 3 \| "full", mono?, action?, hidden? }. copy: true copies a string or number value, a string copies that string, false opts out of copyable. span clamps to columns; hidden items are skipped. |
| `columns?` | `1 \| 2 \| 3` | `2` | Columns once the container is at least 32rem (2) or 48rem (3) wide. Always one column below 32rem, so it stacks on phones and in narrow cards. |
| `layout?` | `"auto" \| "stacked" \| "inline"` | `"auto"` | stacked: label above value. inline: a label column and a value column (from 24rem of container width, so phones stack). auto: inline when there is one column and the container is at least 40rem wide, stacked otherwise. |
| `labelWidth?` | `number` | `160` | Width of the label column in px for the inline layout. Never more than 35% of the row. |
| `variant?` | `"plain" \| "divided" \| "surface" \| "grouped"` | `"divided"` | plain: no lines. divided: hairlines between rows, per column. surface: the whole list, header included, on a raised panel. grouped: iOS inset rows on a panel, label left and value right, 44px rows (ignores columns and layout). |
| `size?` | `"sm" \| "md" \| "lg"` | `"md"` | Label 12 / 13 / 14px, value 13 / 14 / 15px, row padding 8 / 12 / 16px. |
| `copyable?` | `boolean` | `false` | Every item with a string or number value gets a copy button. Items opt out with copy: false; React element values need copy set to a string. |
| `copyLabel?` | `string` | `"Copy"` | The copy button's tooltip and aria-label, followed by the item label: "Copy CUI". |
| `copiedLabel?` | `string` | `"Copied"` | Tooltip and screen reader announcement after a copy. |
| `emptyValue?` | `ReactNode` | `"–"` | Shown, muted, for null, undefined, false and "" values. Empty values are never copyable. |
| `loading?` | `boolean` | `false` | Values become skeleton bars (widths vary by row, the same on the server and in the browser) and the <dl> gets aria-busy. With no items, skeletonCount fake rows show. Skeletons crossfade into the values. |
| `skeletonCount?` | `number` | `6` | Rows to fake while loading before any items exist. |
| `title?` | `ReactNode` | `""` | The header title. With a title the root becomes a <section> labelled by it. |
| `description?` | `ReactNode` | `""` | A muted line under the title. |
| `titleAs?` | `"h2" \| "h3" \| "h4"` | `"h3"` | Heading level of the title, to fit the page outline. |
| `actions?` | `ReactNode` |  | Buttons at the end of the header, like an Edit button. |
| `children?` | `ReactNode` |  | Rows built from parts instead of items: DescriptionItem, or DescriptionTerm and DescriptionDetails for full control (see Parts). |
| `classNames?` | `Partial<Record<"header" \| "title" \| "description" \| "actions" \| "body" \| "grid" \| "item" \| "term" \| "icon" \| "details" \| "value" \| "hint" \| "copy" \| "action" \| "skeleton", string>>` |  | Extra classes for the inner parts. grid is the <dl>, item the row <div>, term the <dt>, details the <dd>, copy the copy button. |
| `className?` | `string` |  | Extra classes for the root, merged last. |

## Parts

### DescriptionItem (component)

One row, for lists written as JSX. Takes the same fields as an item (label, value or children, description, icon, copy, span, mono, action, hidden) and reads size, variant and the copy labels from the list. Without a label its children render as they are, so you can compose the term and details yourself.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `label?` | `ReactNode` |  | The term. Leave it out to write DescriptionTerm and DescriptionDetails as children. |
| `value?` | `ReactNode` |  | The details; children work too. |
| `copy?` | `boolean \| string` |  | A copy button for the value, or for the given string. |
| `span?` | `1 \| 2 \| 3 \| "full"` |  | Columns the row spans in the grid layout. |
| `mono?` | `boolean` |  | Monospaced value, for IDs, IBANs and keys. |

### DescriptionTerm (component)

The <dt> with the label look and an optional icon prop. Use it inside a DescriptionItem without a label.

### DescriptionDetails (component)

The <dd> with the value look: muted, end-aligned and tabular in grouped rows, plain text elsewhere.

### DescriptionListItem (type)

One entry of items: id, label, value, description, icon, copy, span, mono, action and hidden.

## Examples

- **Invoice summary, grouped**: iOS inset rows: label left, value right, a long value moves under its label on a phone.
- **Inline edit**: InlineEdit as the value: click to edit, Enter saves, Escape cancels.
- **Three columns**: An order on a surface panel; three columns from 48rem of container width, the address spans the row.
- **Loading**: Labels stay, values are skeletons that crossfade in after 1.2s. Reload to replay.
- **Inline profile, small**: layout="inline", size="sm", copyable with custom copy labels; stacks below 24rem (phones).
- **Narrow container**: A 280px sidebar card stacks into one column on desktop too; built from DescriptionItem parts.

## See also

- [Copy Button](https://wingo-ui.com/components/copy-button) ([Markdown](https://wingo-ui.com/components/copy-button.md))
- [Inline Edit](https://wingo-ui.com/components/inline-edit) ([Markdown](https://wingo-ui.com/components/inline-edit.md))
- [Card](https://wingo-ui.com/components/card) ([Markdown](https://wingo-ui.com/components/card.md))
- [Page Header](https://wingo-ui.com/components/page-header) ([Markdown](https://wingo-ui.com/components/page-header.md))
- [Table](https://wingo-ui.com/components/table) ([Markdown](https://wingo-ui.com/components/table.md))

---

Canonical: https://wingo-ui.com/components/description-list
More components: https://wingo-ui.com/components · Index for agents: https://wingo-ui.com/llms.txt
