Persona

Card

Data display · 15 props · Updated Sep 22, 2026

A card is a frame around content that belongs together. children is the only part it needs; the heading, the sections and the footers are yours to pass or leave out. What the card supplies beyond the frame is a set of states — loading, empty, errored, collapsed — that it renders for you.

The props below are CardV2’s. A plain Card also exists with two propselevated and $matchCardV2 — and it is the older container rather than what this page documents. Its source marks it deprecated, and it is still load bearing: a Tooltip’s surface is that component with a shadow on it, so the old card cannot be deleted while tooltips exist.

  • Passport, issued 2021, expires 2031.
    Updated 2 days ago
    Passport, issued 2021, expires 2031.
    Updated 2 days ago
    Container the bordered surface. It supplies the frame and the padding; the rest is yours.
  • Passport, issued 2021, expires 2031.
    Updated 2 days ago
    Passport, issued 2021, expires 2031.
    Updated 2 days ago
    Title (optional) what the card is about, at the top of it.
  • Passport, issued 2021, expires 2031.
    Updated 2 days ago
    Passport, issued 2021, expires 2031.
    Updated 2 days ago
    Actions (optional) what you can do to the whole card, beside the title. actions takes descriptions — a label and a handler — not buttons.
  • Passport, issued 2021, expires 2031.
    Updated 2 days ago
    Passport, issued 2021, expires 2031.
    Updated 2 days ago
    Section title (optional) names a Card.Section. A card with one section does not need one; a card with three does.
  • Passport, issued 2021, expires 2031.
    Updated 2 days ago
    Passport, issued 2021, expires 2031.
    Updated 2 days ago
    Body the content. It is the only required part, and the card has no opinion about it.
  • Passport, issued 2021, expires 2031.
    Updated 2 days ago
    Passport, issued 2021, expires 2031.
    Updated 2 days ago
    Footer (optional) secondary metadata under the content — timestamps, counts, the small print.

Describe the heading with values, not elements

Section titled “Describe the heading with values, not elements”

heading is not a node. It takes title as a string, plus actions, menuActions, sticker, leftDecorativeIcon and headingProps, and PDS builds the elements from them: actions is a list of labels and handlers that becomes buttons, and menuActions becomes a dropdown behind a single overflow control.

Passing a node as title is not a way around that. PDS copies the title into an aria-label, so a node arrives at a screen reader as “[object Object]” — measured while building the figure above.

Section titled “Pick the footer prop by what the footer holds”

footer takes an array and renders it as one paragraph with the items separated by a dot; it is for metadata, which is what the figure’s Footer callout describes. footerContent renders whatever you give it, as is. If the footer contains a button, that is the prop you want.

Use a card to separate a group of content from what surrounds it. That is the whole mechanism, and it is why two of them nested draw the same boundary twice: the inner border stops meaning anything, because there is nothing outside it that it is distinguishing the content from. A page made entirely of cards has the same problem at the other scale.

For structured, comparative data use a Table rather than forcing rows into cards. For separate views of the same area use a Tab Bar, and for a message about the state of the system use a Banner.

A heading action, a body link and a whole-card click are three targets inside one boundary, and a reader aiming at the card cannot tell which one they will hit. Pick one. Where the card itself is the target, it is a single interactive element wrapping the content rather than a handler on the container, which is also the only version a keyboard can reach.

The card owns its loading, empty and error states

Section titled “The card owns its loading, empty and error states”

loading draws a shimmer in place of the content, at 200px unless loadingProps.height says otherwise. empty replaces it with a message that defaults to “No data collected” and is set by emptyProps.message. Both are the card’s job rather than the caller’s, so a card does not need a wrapper deciding which of three trees to render.

errored forces the error state, and the card is also an error boundary for its own content: anything that throws inside it renders the same state instead of taking the page down. One asymmetry to know about — erroredProps.onError fires only for errors that bubble up from children, not when you set errored yourself.

collapsible with defaultCollapsed adds a fourth state, a card that starts closed. The chevron that opens it is transparent until the heading is hovered or focused, so a collapsed card does not currently look collapsible from across the page.

Turn off sticky when the card should scroll as one piece

Section titled “Turn off sticky when the card should scroll as one piece”

sticky defaults to true, which is the opposite of how the props table reads: a card’s heading and footer pin themselves while the body scrolls unless you pass sticky={false}. That is the right default for a long record and the wrong one for a short card inside a scrolling page, where a heading pins for 40px and then releases.

fullHeight renders the content at full height; the prop’s own description calls it a hack for cards that don’t use sections.

Card.Stacked takes several cards and renders them as one surface with dividers between them. Card.Section adds a section inside a card, with its own heading, loading and empty states.

The card’s own heading renders as h2 and a section heading as h3, chosen so the document outline matches the visual one. That holds only if the card sits directly under the page’s h1. Where it sits deeper, headingProps.as overrides the level, and it is the only way to — the level is not derived from where the card is on the page.

The error state’s default text is ”🔍 Oops, something went wrong loading this card.” — an emoji, an apology and no information about what failed or what to do next. Set erroredProps.message to something that says which part of the record could not load. See Writing for what a message owes the reader.

NameType
footer

ReactNode[]

Optional footer to render at the bottom of the card. Footer content is rendered as a paragraph with the items separated by a dot.

footerContent

ReactNode

Optional arbitrary footer content to render as-is.

sticky

boolean

Whether the card heading and footer are sticky.

errored

boolean

Force the card to render in an errored state. Errors bubbling up from children will also render the errored state.

erroredProps

{ message?: string | undefined; onError?: ((error: Error) => void), undefined; }

Props for the errored state.

containerProps

any

fullHeight

boolean

Whether the card should render its content with 100% height. This is a bit of a hack for cards that want full control of their content and don’t use sections.

children

ReactNode

heading

Pick<{ isTopSection: boolean; sectionHasContent: boolean; hasFooter: boolean; loading: boolean; title: string; sticker?: ReactElement<unknown, string | JSXElementConstructor<any>> | undefined; … 6 more …; headingProps?: (Omit<…> & { …; }) | undefined; }, “title” | … 5 more … | “headingProps”>

Available props for the section’s heading.

collapsible

boolean

Whether the section is collapsible.

defaultCollapsed

boolean

Whether the section is initially collapsed.

loading

boolean

If true, the section will be rendered in a loading state.

loadingProps

{ height?: string | undefined; }

Set height of the loading state.

empty

boolean

Control display of the empty state.

emptyProps

{ message?: string | undefined; }

Props for the empty state.

Was this page helpful?