---
title: Card List
slug: card-list
kind: pattern
summary: Records shown as individual bordered blocks rather than table rows — right for a few important, visual or heterogeneous things, and wrong for forty similar ones.
problem: >-
  Cards get chosen because they photograph well, then applied to a list of forty
  homogeneous records. Nothing lines up, nothing can be compared, and a screen
  that should have been scannable becomes a scroll marathon.
family: [scan]
data_shape: [collection]
principles: [minimize-distance, progressive-disclosure]
interaction: [scanning]
density: low
complexity: low
status: stable
visibility: public
use_when:
  - There are few records — roughly under fifteen — and they will stay few.
  - Records are heterogeneous, so a shared column set would be half empty.
  - The record's value is visual — a photo, a chart, a preview, a floor plan.
  - Each item is individually important and chosen deliberately, not compared in bulk.
avoid_when:
  - Records are homogeneous and people compare them. Use a table.
  - The list will grow. Cards degrade with volume in a way tables do not.
  - The main job is finding one row, which is scanning, and scanning wants alignment.
alternatives:
  - slug: dense-operational-table
    when: Records share the same fields and people compare across them. This is the usual answer.
  - slug: grouped-list
    when: The records are similar but fall into meaningful sets that should be visually separated.
ask_leo: |
  Render this collection as cards rather than table rows, and keep them
  disciplined.

  - Give every card the same internal structure in the same order, so the eye
    learns one shape and reuses it.
  - Put the identifying name first and make it the largest thing on the card.
  - Limit each card to about five elements. A card is not a place to show the
    whole record.
  - Keep cards a consistent height in a row so the grid does not ragged out.
  - If the record has an image, it is the reason to use a card — give it real
    space rather than a thumbnail in a corner.
  - Make the whole card the click target, not just the title.
  - If this collection can exceed about fifteen items, build a table instead and
    reserve cards for a small featured set above it.
related:
  - title: Dense Operational Table
    url: /patterns/dense-operational-table
    summary: The alternative, and the right answer more often than teams expect.
---

## Anatomy

```
┌──────────────────┐  ┌──────────────────┐  ┌──────────────────┐
│ [ image ]        │  │ [ image ]        │  │ [ image ]        │
│ Riverside Fit-Out│  │ Harbour Depot    │  │ Kestrel Joinery  │
│ Leeds · Dana     │  │ Hull · Sam       │  │ Grimsby · Priya  │
│ 6 open jobs      │  │ 2 open jobs      │  │ 1 open job       │
└──────────────────┘  └──────────────────┘  └──────────────────┘
   same structure, same order, same height, every card
```

- **One shape, repeated.** The eye learns the card layout once; every deviation
  costs that learning back.
- **A five-element ceiling.** Cards invite accumulation because there is visible
  space. The space is not free — it is the reason the list scrolls.
- **Consistent height per row**, or the grid ragged-edges and stops reading as a
  set.

## Why it works, when it works

Cards buy **containment**. Each record is visibly one thing, which is exactly
right when records differ from one another — a product with a photo, a report
with a chart, a template with a preview. Containment is what a table cannot
give, because a table's power comes from records sharing a structure.

They also buy space for a genuinely visual payload. A photograph in a table row
is a thumbnail; in a card it is the point.

## Why it usually does not work

For homogeneous records, cards destroy the one thing that makes a collection
readable: **alignment**. Values in a table column share an edge, so the eye
compares them before reading. Cards place every value in its own coordinate
space, so forty records means forty separate readings.

The house rule from the design guidance is direct: for long lists, a condensed
table beats large whitespace-heavy cards; reserve roomy cards for a few
high-importance items. This is the named anti-pattern **card soup** — twenty
homogeneous records rendered as enormous cards, usually chosen because the
mockup with six looked good.

## The test

Ask: **would anyone ever want to compare this field across records?** If yes —
amounts, dates, statuses, counts — those values want a column. If the honest
answer is that people pick one item and act on it, cards are fine.

Then ask how many records there will be in a year. Cards that work at eight
rarely work at eighty, and the migration is a rewrite of the screen.

## Getting it wrong

- **Card soup.** The default failure. Homogeneous rows, huge boxes, no
  comparison possible.
- **Ragged heights**, so the grid never resolves into a set.
- **Cards that grew into records.** Twelve fields per card is a table that has
  been folded up and made unsortable.
- **Only the title clickable**, so most of a large target does nothing.

## Exemplars

**Airbnb search results** are the strongest case for cards: the photograph
genuinely is the information, records are heterogeneous, and people choose one
rather than compare fifty on a metric.

**Figma's file browser** shows the previews-are-the-point argument — a filename
list would be strictly worse because recognising a design by its thumbnail is
the whole task.

**Stripe** is the useful counter-example: almost nothing in the dashboard is a
card, because almost everything is a homogeneous record people compare.

The extractable rule: **cards are for recognition, tables are for comparison.**
Decide which one the person is doing, and the choice makes itself.
