DataList
A list of key and value pairs: an order summary, a profile, a settings pane, the input of a tool call. It is a native dl, so each term and its details are a dt and a dd. The gaps follow density, and the term and details colors, the term weight and the term column width come from the datalist contract tokens.
On this page
<DataList.Root aria-label="Order summary">
<DataList.Item>
<DataList.Term>Order</DataList.Term>
<DataList.Details>#10482</DataList.Details>
</DataList.Item>
<DataList.Item>
<DataList.Term>Status</DataList.Term>
<DataList.Details>Shipped on 2027-01-04</DataList.Details>
</DataList.Item>
<DataList.Item>
<DataList.Term>Total</DataList.Term>
<DataList.Details>$42.00</DataList.Details>
</DataList.Item>
</DataList.Root>
- Order
- #10482
- Status
- Shipped on 2027-01-04
- Ship to
- Mina Park
12 Harbor Street
Busan - Total
- $42.00
Server Components can import it from @tounsoo/todae/data-list. That entry has no 'use client' and no hooks; the main @tounsoo/todae entry is a client module.
DataList.Item is a div that holds one term and its details. A div is valid inside a dl for this, and it is the one row you can style, move or key. Give each item one DataList.Term and one DataList.Details. Several values for one term go inside that one DataList.Details, for example as a list.
Orientation
orientation="vertical" puts the term above its details. The default, "horizontal", puts the term in a column beside the details. The value reaches CSS as data-orientation.
<DataList.Root orientation="vertical" aria-label="Profile">
<DataList.Item>
<DataList.Term>Name</DataList.Term>
<DataList.Details>Mina Park</DataList.Details>
</DataList.Item>
<DataList.Item>
<DataList.Term>Email</DataList.Term>
<DataList.Details>mina@example.com</DataList.Details>
</DataList.Item>
</DataList.Root>
- Name
- Mina Park
- mina@example.com
- Teams
- Design systems, Docs
Columns
minItemSize lays the items out in columns, each no narrower than the CSS length you give it, such as "10rem". As many columns fit as the width allows, so the list reflows without a media query, and in a list narrower than minItemSize the single column shrinks to fit. Items fill the rows in DOM order, so the reading order stays the source order. The gap between columns is datalist.gap.between-columns, and the gap between rows stays datalist.gap.row. Grid can't lay out a list's items, since it renders a div and the items have to be children of the dl.
<DataList.Root orientation="vertical" minItemSize="10rem" aria-label="Plan">
<DataList.Item>
<DataList.Term>Plan</DataList.Term>
<DataList.Details>Team</DataList.Details>
</DataList.Item>
<DataList.Item>
<DataList.Term>Seats</DataList.Term>
<DataList.Details>12 of 20</DataList.Details>
</DataList.Item>
{/* … */}
</DataList.Root>
- Plan
- Team
- Seats
- 12 of 20
- Storage
- 48 GB of 100 GB
- Billing
- Monthly
- Renews
- 2027-02-01
- Support
- Business hours
Each item keeps its orientation inside its column. A horizontal item stacks when its column is narrower than the term column, datalist.gap.column and the details minimum together, so give columns of horizontal items a minItemSize of at least calc(var(--todae-datalist-term-width) + var(--todae-datalist-gap-column) + var(--todae-datalist-details-min-width)).
In a parent that sizes the list to its content, the number of columns follows the width of the items' text; the next section says how to give the list a width.
Narrow widths
A horizontal list stacks by itself when it is too narrow for the term column and a readable details column. The term takes up to datalist.term.width, and shrinks in a list narrower than that column. The details wrap below it once they would be narrower than datalist.details.min-width. No container query is involved, so a company can change both tokens. The gap between a stacked term and its details is datalist.gap.stacked.
The list takes the width its parent gives it. A parent that sizes the list to its content, such as a flex row or an inline-block, gives it only the width of its text, so its rows stack. Give the list a width there, for example flex: 1 in a flex row.
Accessible composition
Todae handles: The dl, dt and dd elements, which expose each dt as a term and each dd as its definition or details, and assistive technology can report the list as a description list. Nothing is added with ARIA, and the div around each pair is invisible to the accessibility tree.
You supply: A name for the list when it needs one, with aria-label or aria-labelledby. Term text that makes sense on its own, such as "Total" or "Ship to".
Watch for: Content that is not a term inside DataList.Term, such as a button or a form control: the dt is exposed as the term for its details. A DataList that holds data with several columns per row; that is a table, which gives each cell its column header.
When to use
When: A set of named values for one thing, such as an order, a person, a plan or a tool call's input.
When not: Rows of the same fields for many things → Table. Values the user edits → Field.
Props
| Name | Type | Description |
|---|---|---|
orientation | "horizontal" | "vertical" | Sets data-orientation. Horizontal puts each term in a column beside its details, and stacks them when the list is too narrow for both. Vertical puts the term above its details. Default: "horizontal" |
minItemSize | string | Lays the items out in columns no narrower than this CSS length, such as "24rem". As many columns fit as the width allows, with datalist.gap.between-columns between them, and a single column never overflows. A horizontal item stacks in a column narrower than its term column, datalist.gap.column and details minimum together. Without it the items sit in one column. |
children | React.ReactNode | DataList.Item parts. |
| Name | Type | Description |
|---|---|---|
children | React.ReactNode | One DataList.Term, then one DataList.Details. |
| Name | Type | Description |
|---|---|---|
children | React.ReactNode | The term, as text. |
| Name | Type | Description |
|---|---|---|
children | React.ReactNode | The value. Put a list of values in one DataList.Details. |