On this page
Summary List
Display labelled data for users to review.
- Overview
- Rules and guidance
- Properties
- Styles
- Accessibility
When to use
- On a review or confirmation page to show what a user has entered before submitting
- To display the key attributes of a single entity, for example an account's name, address, and tariff
- Anywhere a set of labelled values needs to be presented in a structured, readable format
When not to use
- To compare data across multiple entities - use Data Table instead. Summary List is for a single entity's attributes, not a grid of items.
- For a plain list of items without labels - use List instead.
- For a small number of inline key-value pairs within body copy - use plain text instead to avoid adding structural overhead to simple content.
Anatomy
- Label
- Value
Basic usage
Edit live code
<SummaryList> <SummaryItem label="Name">David Attenborough</SummaryItem> <SummaryItem label="Age">94</SummaryItem> </SummaryList>
Variants and features
Rich values
Values can contain any React content, including formatted numbers, Badges, Text Links, or other inline components.
Edit live code
<SummaryList> <SummaryItem label="Account">A-001234</SummaryItem> <SummaryItem label="Status"><Badge variant="success">Active</Badge></SummaryItem> <SummaryItem label="Supply address"> 1 Example Street, London, EC1A 1BB </SummaryItem> </SummaryList>
Content
Labels
Labels identify what the value represents. Keep them short and noun-based, for example "Name", "Date of birth", "Account number". Avoid full sentences or questions as labels.
Values
Values should be unambiguous. For empty or inapplicable values, use a short descriptive phrase such as "None" or "Not applicable" rather than leaving the space blank. This makes it clear the absence of a value is intentional.
Action links
When a value row includes an action link such as "Change" on a review page, include context about what is being changed in the link text. The simplest way to do this is to add a <VisuallyHidden> span with the label name, for example:
Edit live code
<SummaryList> <SummaryItem label="Name"> David Attenborough <TextLink href="/change/name"> Change <VisuallyHidden>name</VisuallyHidden> </TextLink> </SummaryItem> <SummaryItem label="Date of birth"> 8 May 1926 <TextLink href="/change/date-of-birth"> Change <VisuallyHidden>date of birth</VisuallyHidden> </TextLink> </SummaryItem> </SummaryList>
This gives screen reader users "Change name" rather than hearing the word "Change" repeated for every row without context.
usage
- SummaryList should be used to display attributes of a single entity, not to compare multiple entitiesSummaryList renders a definition list, a semantic structure for one subject and its properties. Comparing multiple items across columns requires a different structure. Use DataTable instead.Copy rule ID for summarylist.single-entity-only
- SummaryList must not be used in place of DataTable for data that compares multiple items of the same typeSummaryList lacks column headers and tabular semantics. Using it for comparative data makes it harder to scan and removes the accessibility benefits of a proper table structure.Copy rule ID for summarylist.no-table-replacement
content
- SummaryList will render as a dl element, with each SummaryItem label as a dt and each value as a dd. Screen readers announce this structure as a description listCopy rule ID for summarylist.will-dl-semantics
- SummaryItem must always include a labelThe label prop renders as a dt element and is required by the TypeScript type. A SummaryItem without a label produces an orphaned dd with no associated term, which is invalid HTML semantics.Copy rule ID for summarylist.label-required
- SummaryItem label should use a concise noun or noun phrase, not a question or full sentenceLabels are scanned quickly before reading the corresponding value. Questions and full sentences slow this scanning pattern and add visual weight disproportionate to the role of a label.Copy rule ID for summarylist.label-nouns
- SummaryList should use labels in a consistent grammatical format throughout the listMixing nouns, verbs, and questions within a single list disrupts the visual rhythm and makes the list harder to scan. Users rely on the consistent structure of labels to navigate efficiently.Copy rule ID for summarylist.label-consistent-format
- If the value contains a link that triggers an edit actionSummaryItem action link should include the label name as visually hidden text within the link so screen reader users hear "Change [label]" rather than repeated "Change" links with no contextA row of identical "Change" links is ambiguous when navigated by keyboard or screen reader. Adding visually hidden context ("Change name", "Change address") removes the ambiguity without affecting sighted users.Copy rule ID for summarylist.action-link-context
- SummaryItem value should not be left visually blank when a value is absent or not applicableAn empty value gives no indication of whether the data is missing, unavailable, or intentionally absent. Use a short descriptive phrase such as "None" or "Not applicable" to signal intentional absence clearly.Copy rule ID for summarylist.no-empty-values
SummaryList
| Name | Type | Default | Required | Description |
|---|---|---|---|---|
as | ElementType | — | No | Renders the component as a different element or component, e.g. `as="section"`. |
forwardedAs | ElementType | — | No | Like `as`, but passed on to the component being wrapped, for when a styled wrapper should keep its own element and change the one beneath it. |
SummaryItem
| Name | Type | Default | Required | Description |
|---|---|---|---|---|
label | ReactNode | — | Yes | Term shown in the item's label (`dt`), with the children shown as its value (`dd`). |
as | ElementType | — | No | Renders the component as a different element or component, e.g. `as="section"`. |
forwardedAs | ElementType | — | No | Like `as`, but passed on to the component being wrapped, for when a styled wrapper should keep its own element and change the one beneath it. |
This component has no styles of its own to document.
In brief
Summary List is a native description list. A screen reader announces each label as a term and its value as the definition. It never takes focus.
What Nebula provides
- Each label is a dt and each value a dd inside a dl, so the pairing is in the markup and not only in the layout.
Accessible design
Write each label so it names its value without the items around it
A screen reader user can move from one term to the next, so "Delivery address" works and "Address 2" does not.
WCAG: 2.4.6 Headings and Labels
Write every value as text
An icon or a colour alone does not reach a screen reader, so the value has to be in the text of the item.
WCAG: 1.4.1 Use of Color 1.1.1 Non-text Content
Accessible development
Keep SummaryItem as the only child of a SummaryList
SummaryList renders a dl, which only allows terms and definitions. Other children break the list structure a screen reader announces.
WCAG: 1.3.1 Info and Relationships
Accessibility testing status
Reviewed on 9 October 2026 against v17.0.1.
| Test | Status | Tested with |
|---|---|---|
| Default state | Tested | axe-core via the Storybook test runner (CI) |
| Advanced states | Not tested | — |
| Keyboard navigation | Manually tested | keyboard (reported by the owner) |
| Screen reader | Manually tested | VoiceOver with Chrome (reported by the owner) |
What these tests and statuses mean
The table records which checks were run on this component on its own. It is not a statement of WCAG conformance, and it does not cover how the component is used.
The tests
- Default state: The component as it first appears, before anything is clicked or changed.
- Advanced states: Every other state and variant: error, open, selected, each size and style, and hovering.
- Keyboard navigation: Whether every function can be reached and used with the keyboard alone, with a visible focus ring.
- Screen reader: Whether a screen reader announces what is on screen correctly: the name, the role and any state.
The statuses
- Tested: Every check in this row passed in an automated run that repeats on every pull request.
- Manually tested: Every check in this row was run once, by a person or with a tool they started, and all of them passed. It is not re-run on every change.
- Partially tested: Some checks in this row were run once and passed; the rest are still to do.
- Issue found: A check in this row failed. The ticket is shown beside the status.
- Not tested: No check in this row has been run yet.
- Not applicable: This row does not apply to the component, for the reason given beside it.