Blog banner
Blog

Data Tables: How to Build an Enterprise-Grade Data Display System

UI

Ui Pirate

2 views · 31 min read

31 min read  |  Today


The "Please Stop Building the Same Table Nineteen Times" Edition

By Vishal Anand (Founder) & Syed Musuddiq (Head of Design), UI Pirate

Part of the Enterprise Product Systems series. Tutorial 02, after How to Build an Enterprise-Grade Design Token System.


Somewhere in your product right now, there is a table with 27 columns, a horizontal scrollbar the size of a toothpick, and a user quietly wondering if they're the problem.

They're not. Neither are you. And neither is the table.

Here's the sentence that starts most enterprise UI disasters: "Can we just add one more column?" Nobody has ever said it with bad intentions. And nobody has ever said it once. By column 27 you have a horizontal scroll, a mobile layout that is basically a ransom note, and three other tables in the same product that handle the same problem in three completely different ways.

So here's the uncomfortable truth we're going to spend this whole ebook on: you don't have a table problem. You have a "we've built the same table nineteen times" problem.

The fix isn't a better table. It's realizing that a table was never the thing you needed to design in the first place.

What you need is a data display system: one set of rules that decides how any enterprise record shows up, as a dense sheet, as a rich card, as an expandable record, or as a full detail page, across 5 fields or 25, 20 records or 20,000, one resource or fourteen.

This is how you build it, the enterprise kind, the kind that survives growing schemas, nested data, four user roles, a mobile app and one very opinionated stakeholder who still wants "just one more column."


What the System Must Survive

Before we design anything, let's write down what this thing has to live through. Think of it as the stress test your system gets at the worst possible Friday:

  • 5 fields today → 25 fields later

  • short text → absurdly long text

  • people → companies → visits → devices → approvals

  • avatars → flags → chips → statuses → links

  • one row → expandable detail

  • 20 records → 20,000+ records

  • different resources with completely different schemas

  • Sheet View ↔ Card View without redesigning each table

  • different user permissions

  • different record states

  • incomplete or missing data

  • nested relationships and child collections

If your table solves only today's visitor list, you built a screen. If it provides the rules for every table that comes after, you built a system. The goal here is the second one.

[Image 1]


What Is an Enterprise Data Display System?

Here's the mindset shift. If you only take one thing from this ebook, take this:

A table is not a component. A table is a configured data-display surface.

Instead of designing "the Visitors table," you design a system of layers, exactly like a good token system:

Diagram
ENTERPRISE DATA DISPLAY SYSTEM
│
├── 01. Foundations
│   ├── Data primitives
│   ├── Spacing
│   ├── Typography
│   ├── Color
│   ├── Radius
│   ├── Borders
│   ├── Elevation
│   └── Iconography
│
├── 02. Semantic Data Tokens
│   ├── Primary data
│   ├── Secondary data
│   ├── Metadata
│   ├── Muted data
│   ├── Interactive data
│   ├── Status
│   ├── Warning
│   ├── Error
│   └── Success
│
├── 03. Data Primitives
│   ├── Text
│   ├── Number
│   ├── Date
│   ├── Date + Time
│   ├── Avatar
│   ├── Flag
│   ├── Icon
│   ├── Link
│   ├── Badge
│   ├── Chip
│   ├── Status
│   ├── Progress
│   └── Actions
│
├── 04. Data Cells
│   ├── Text Cell
│   ├── Person Cell
│   ├── Status Cell
│   ├── Date Cell
│   ├── Location Cell
│   ├── Tag Cell
│   ├── Link Cell
│   ├── Number Cell
│   ├── Mixed Cell
│   └── Custom Cell
│
├── 05. Table System
│   ├── Table Shell
│   ├── Toolbar
│   ├── Header
│   ├── Row
│   ├── Cell
│   ├── Selection
│   ├── Sorting
│   ├── Filtering
│   ├── Pagination
│   └── Actions
│
├── 06. Card System
│   ├── Record Card
│   ├── Identity
│   ├── Primary Fields
│   ├── Metadata
│   ├── Status
│   ├── Actions
│   ├── Expand
│   └── Details
│
└── 07. Data Display Patterns
    ├── Sheet View
    ├── Card View
    ├── Empty
    ├── Loading
    ├── Error
    ├── No Results
    ├── Selected
    └── Expanded

Same philosophy as tokens: establish foundations, define meaning, build reusable primitives, compose them into cells and views, and let each product configure the system instead of inventing new components.

Real tables (Visitors, Devices, Companies) become configurations. Not new inventions. It's the difference between a restaurant with a menu and a restaurant that rebuilds the kitchen every time someone orders.

[IMAGE 3 - The System Layers] (DIAGRAM)


Why Enterprise Tables Fail

A basic table assumes this:

Text
Record → Row → Columns

An actual enterprise record looks like this:

Diagram
Record
├── Identity
├── Organization
├── Status
├── Relationships
├── Dates
├── Location
├── Metadata
├── Permissions
├── Actions
├── Attachments
├── Nested records
└── Audit information

That's not a row. That's a small novel. And the failure happens the second all of it gets equal visual weight, like a group chat where every message is in bold.

The result:

  • excessive horizontal scrolling

  • unreadable rows

  • inconsistent actions

  • too many icons, doing nothing

  • unclear hierarchy

  • duplicated information

  • difficult mobile behavior

  • inconsistent card designs

  • poor handling of missing data (blank, dash, N/A, "null", and the occasional "undefined" that somehow reached production)

  • tables that cannot scale to new record types

Notice what's missing from that list: "the table wasn't pretty enough." Prettiness was never the problem. And here is the actual goal:

The goal is not to fit all the data into the UI. The goal is to expose the right information at the right level of detail for the user's current task.

[IMAGE 4 - The Record Is a Novel] (FIGMA-STYLE) AI generation prompt:


Table vs List vs Card (Not Interchangeable)

Different representations solve different problems. Picking the wrong one is like bringing a spreadsheet to a first date.

Pattern

Optimized for

Typical use

Table / Sheet

Comparison

Admin, operations, bulk review

List

Sequential scanning

Activity, messages, search results

Card

Recognition

People, companies, resources

Expanded Card

Context

Secondary information

Detail Page

Investigation

Complete record management

Sheet View is for when users compare many records across the same fields. Think high density, predictable columns, minimal decoration, strong alignment, sorting, filtering, selection, bulk actions.

Card View is for when users need to understand records individually. Think strong identity, richer field rendering, semantic icons, avatars, flags, chips, grouped metadata, progressive disclosure.

And the classic crime: a card is not a detail view.

A card answers: What is this record and what matters right now? A detail page answers: Tell me everything about this record.

Once your card has 19 fields, three tabs and a scrollbar, congratulations, you built a page and put it in a trench coat.

[IMAGE 5 - A Card vs a Page in a Trench Coat]


The Central Architecture (The Big One)

Here is the rule that holds this entire ebook together:

Sheet View and Card View are not two independent systems. They are two renderers of the same data model.

Text
Data
+
Schema
+
View Configuration
+
Renderer
=
UI

The same dataset must be able to render as this:

Diagram
┌──────────────────────────┐
│ SHEET VIEW               │
│ Name | Company | Status  │
└──────────────────────────┘

or as this:

Diagram
┌──────────────────────────────────────────────┐
│ [Avatar] John Smith              ● Checked In│
│          Acme Corporation                    │
│                                              │
│ Host       Sarah Williams                    │
│ Visit      Client Meeting                    │
│ Date       02 Oct · 10:30 AM                 │
│                                              │
│ View details                           ⋯     │
└──────────────────────────────────────────────┘

Same record. Different renderer. Like one person who is "Work You" in a meeting and "Family WhatsApp Group You" at 11pm.

The full flow:

Diagram
ENTERPRISE DATA
      ↓
 DATA MODEL
      ↓
   SCHEMA
      ↓
┌─────────────┬─────────────┬─────────────┐
│ FIELD TYPE  │  PRIORITY   │  BEHAVIOR   │
└─────────────┴─────────────┴─────────────┘
      ↓
VIEW CONFIGURATION
      ↓
┌──────────────────┬──────────────────┐
│  SHEET RENDERER  │  CARD RENDERER   │
│  Comparison      │  Recognition     │
│  Dense           │  Rich            │
│  Structured      │  Hierarchical    │
└──────────────────┴──────────────────┘
      ↓
USER ACTIONS

And the same records can go further, as four levels of detail:

  • Sheet View: dense, comparison-oriented, structured data

  • Card View: recognition-oriented, hierarchical, richer presentation

  • Expanded View: progressive disclosure of secondary and extended information

  • Detail View: complete investigation, when the card is no longer enough

This prevents the single most expensive mistake in enterprise UI:

Text
VisitorTable
EmployeeTable
DeviceTable
CompanyTable

as four completely separate UI systems. Instead:

Text
EnterpriseDataView
        +
     Schema
        +
ViewConfiguration

One engine. Many configurations. Fewer 2am bug reports.

[IMAGE 6 - Hero: One Data Model, Two Renderers]


Shared State: The Workspace Owns the Query

Where do search, filters, sort and selection live?

Wrong answer: inside the table. Right answer: in the data workspace. The renderer just consumes it.

Diagram
Dataset
  ↓
Search
  ↓
Filters
  ↓
Sort
  ↓
Selection
  ↓
Pagination
  ↓
Renderer
  ├── Sheet
  └── Card

So this must just work:

Diagram
Select 12 records
      ↓
Switch Sheet → Card
      ↓
12 records remain selected

Same for search, filters, sort and page context where practical. If a user selects 12 rows, flips to card view, and the app forgets everything like a goldfish, that isn't a view switch. That's a factory reset.

Give the whole thing a first-class config object, DataViewConfig:

Diagram
DataViewConfig
├── resource
├── view
├── density
├── visibleFields
├── fieldOrder
├── fieldPriority
├── sort
├── filters
├── selection
├── pagination
└── actions

And the view structure around it:

Diagram
DataView
│
├── DataSource
│
├── Query State
│   ├── Search
│   ├── Filters
│   ├── Sort
│   ├── Pagination
│   └── Selection
│
└── Renderer
    ├── SheetRenderer
    └── CardRenderer

[IMAGE 7 - Shared Query State] (DIAGRAM) AI generation prompt:


Data Tokens (Yes, Tokens Again)

If you read our design tokens ebook, you know where this is going. Same three layers, new costume. And if you haven't read it yet, our full guide on how to build an enterprise-grade design token system covers the foundations this whole ebook runs on: primitive, semantic and component tokens, aliases, themes, multi-brand setups and governance. Read it first or read it after, but read it.

Primitive tokens (raw ingredients):

Tokens
color.blue.500
color.gray.100
spacing.100
spacing.200
spacing.300
radius.sm
radius.md
font.size.sm
font.size.md

Semantic tokens (what the value is for):

Tokens
data.text.primary
data.text.secondary
data.text.muted
data.text.link

data.surface.default
data.surface.hover
data.surface.selected

data.border.default
data.border.subtle

data.status.success
data.status.warning
data.status.error
data.status.info

Think of the semantic data tokens as the categories of meaning your data can carry: primary data, secondary data, metadata, muted data, interactive data, and status (with its success, warning and error flavors).

Component tokens (closest to the UI):

Tokens
table.header.height
table.row.height
table.cell.padding.x
table.cell.padding.y

card.padding
card.gap
card.radius

record.summary.gap
record.details.gap
record.action.size

Why bother? Because when someone says "can we make everything denser," the answer is a token change, not a three-week redesign of nineteen tables. Same for branding and accessibility.

[IMAGE 8 - Data Token Layers] (FIGMA UI) AI generation prompt:


Data Primitives: Build the Vocabulary First

Before you build a single table, build the vocabulary. You wouldn't write a novel before learning the alphabet (although some roadmaps clearly tried).

Basic types: Text, Number, Currency, Percentage, Boolean, Date, Time, DateTime

Identity: Person, Company, Organization, Team, Avatar

Location: Country, City, Address, Building, Floor, Room

Classification: Status, Tag, Category, Priority, Type

Interactive: Link, Email, Phone, URL, Action, Menu

Rich content: Image, Document, Attachment, Icon, Progress, Rating

Composite fields:

Text
Avatar + Name
Person + Company
Flag + Country
Status + Timestamp
Icon + Label

Composite fields are where the magic happens, and also where designers get overexcited. See the rule about decorative icons coming up.

[IMAGE 9 - Data Primitive Board] (FIGMA-STYLE) AI generation prompt:


Field Types vs Presentation

A field's data type should not dictate one fixed visual treatment.

Diagram
Country
  ↓
COUNTRY field type
  ↓
Sheet: India
Card:  🇮🇳 India
Diagram
Host
  ↓
PERSON field type
  ↓
Sheet: Sarah Williams
Card:  [Avatar] Sarah Williams
Diagram
Status
  ↓
STATUS field type
  ↓
Sheet: Checked In
Card:  ● Checked In

Three compositional levels:

Diagram
Data Primitive
      ↓
Data Cell
      ↓
View Renderer

And every field gets a proper definition, no vibes allowed:

JSON
{
  "key": "host",
  "label": "Host",
  "type": "person",
  "priority": "P2",
  "sortable": true,
  "filterable": true,
  "searchable": true,
  "interactive": true
}

A field should know: key, label, type, priority, sortable, filterable, searchable, interactive, sheetVisibility, cardVisibility, renderer.

[IMAGE 10 - One Field, Two Presentations] (FIGMA-STYLE) AI generation prompt:


Data Cells: Primitives Get a Job

Primitives are single ingredients. Data cells are the prepared dishes, the reusable units that both Sheet and Card renderers consume.

Text
Text Cell
Person Cell
Status Cell
Date Cell
Location Cell
Tag Cell
Link Cell
Number Cell
Mixed Cell
Custom Cell

A Person Cell knows how to show an avatar, a name and maybe a company line. A Status Cell knows how to show an indicator plus a label. A Mixed Cell combines two primitives (Status + Timestamp, say). A Custom Cell is the escape hatch for the weird one-off, built from the same primitives instead of from scratch.

This is why you can add a new resource to the product without a new design sprint. The cells already exist. You're just choosing which ones to use.

[IMAGE 11 - The Cell Library] (FIGMA-STYLE) AI generation prompt:


Anatomy of a Data Workspace

A real enterprise data screen has far more than rows and columns:

Diagram
Data Workspace
├── Page Header
├── Search
├── Filters
├── Active Filters
├── View Switcher
├── Density Control
├── Column Configuration
├── Selection / Bulk Actions
├── Data Surface
│   ├── Header
│   ├── Rows / Cards
│   └── Expansion
└── Footer
    ├── Result Count
    ├── Page Size
    └── Pagination

And the toolbar rule: do not put every possible control in the primary toolbar. Prioritize:

  1. Search

  2. Most common filters

  3. View switcher

  4. Primary action

  5. Secondary controls

  6. Configuration

Everything else lives in overflow. The toolbar is not a junk drawer.

[IMAGE 12 - The Workspace Anatomy] (FIGMA-STYLE with callouts) AI generation prompt:


Sheet View: The Comparison Mode

Sheet View exists for one reason: comparison. A user looks across many records at the same fields and spots the difference. Its visual language should be deliberately restrained. It should feel like a reliable information surface, not a carnival.

Prefer: normal text, clear alignment, consistent number formatting, restrained status indicators, links in the interactive color, predictable column widths, compact density.

Avoid: decorative icons everywhere, unnecessary chips, oversized avatars, excessive color, inconsistent cell layouts.

Alignment defaults:

Data

Alignment

Text

Left

Person

Left

Date

Left or right, depending on the system

Number

Right

Currency

Right

Status

Left

Actions

Right

The important rule is consistency. A table where numbers randomly align left, right and center is not "expressive," it's a hostage note.

Column width: every column gets a minimum, preferred and maximum width where appropriate. Long content should never destroy the entire table, like one guest standing in a doorway at a party.

[IMAGE 13 - Sheet View Anatomy] (FIGMA-STYLE with callouts) AI generation prompt:


Card View: The Recognition Mode

A good card answers four questions, in order:

  1. What is this record?

  2. What is its current state?

  3. What is the most important context?

  4. What can I do with it?

Diagram
┌──────────────────────────────────────────────┐
│ [Avatar] John Smith            ● Checked In  │
│          Acme Corporation                    │
│                                              │
│ Host       Sarah Williams                    │
│ Visit      Client Meeting                    │
│ Date       02 Oct · 10:30 AM                 │
│                                              │
│ View details                           ⋯     │
└──────────────────────────────────────────────┘

Cards can be richer than rows: avatars, flags, chips, status indicators, semantic icons, grouped metadata, richer links, stronger hierarchy, contextual actions. But here's the rule that separates adults from interns:

Richness must be semantic, not decorative.

A flag is allowed because it communicates a country. An avatar is allowed because it communicates identity. A status indicator is allowed because it communicates state. An icon next to every field "because the card looks empty" is not design. That is panic with a stroke width.

So the two views have deliberately different visual languages: Sheet is information-first (text, numbers, dates, links, status, alignment, density, comparison). Card is recognition plus context (avatars, flags, chips, hierarchy). Same data, different personality, same rules underneath.

[IMAGE 14 - Anatomy of a Record Card] (FIGMA-STYLE with callouts) AI generation prompt:


Information Priority: The Secret Sauce

This is the mechanism that lets one system handle a table with 4 fields and a table with 25 without a breakdown. Every field gets a priority, as part of the data model, not as a visual afterthought.

Text
P0 — Identity
P1 — Primary
P2 — Secondary
P3 — Extended

P0 — Identity. What is the record? Name, title, company, primary identifier. Always visible.

P1 — Primary. What matters during normal scanning? Status, date, owner, visit type, location. Normally visible.

P2 — Secondary. Useful supporting information. Country, phone, email, department, host. Visible when space and context justify it.

P3 — Extended. Useful for investigation. Internal ID, created by, modified by, audit metadata, secondary timestamps. Normally inside expansion.

Think of a newspaper: the headline (P0), the lede (P1), the body (P2), and the fine print nobody reads unless something went wrong (P3).

[IMAGE 15 - The Priority Ladder] (DIAGRAM) AI generation prompt:


The Card Layout Algorithm

Please, for the love of all that is holy, do not use this rule:

"Always show the first four columns."

It fails with two fields. It fails with twenty-five. It fails when the first four columns happen to be Created By, Modified By, Internal ID and Row Number.

Use this:

Text
1. Always show P0.
2. Add the highest-value P1 fields.
3. Add P2 fields only when they improve recognition.
4. Respect the available card width.
5. Move remaining fields into Details.
6. If no fields are hidden, do not render an expansion control.
Diagram
P0
+
High-value P1
+
Selected P2
↓
Collapsed Card

Four-field dataset (Name, Company, Status, Date): show all four. Do NOT invent a "Show more" with nothing behind it. That's a door painted on a wall.

Twenty-field dataset: the collapsed card shows Name, Company, Status, Date, Host, then:

Text
+ 15 more details

The expanded state reveals the rest. The card is driven by information priority and available space, not by an arbitrary number someone picked in a meeting.

[IMAGE 16 - Four Fields vs Twenty Fields] (FIGMA-STYLE) AI generation prompt:


Expandable Records: Progressive Disclosure Done Right

Diagram
Collapsed
  ↓
Important information
  ↓
Expand
  ↓
Supporting / extended information

The rules:

  • Prefer one expansion level: Card → Details.

  • Preserve the card header, identity and status when expanded.

  • Reveal additional fields, keeping the same field order.

  • Let the user collapse it again.

  • Don't move the user to another context unless necessary.

On the label, use meaningful counts when they help ("Show 8 more", "View all details"). If the number of hidden fields is dynamic, the count quietly communicates scale. A "Show more" with no number is a mystery box, and nobody trusts the mystery box.

[IMAGE 17 - Collapsed vs Expanded] (FIGMA-STYLE) AI generation prompt:


Nested Data and Nested Tables

Enterprise data is relational:

Diagram
Company  → Employees
Visit    → Visitors
Visitor  → Visits
Device   → Activity
Building → Rooms

The temptation is to put the child table inside the parent card. Like Russian dolls, except every doll has a scrollbar.

Nested field vs nested collection

Nested field: a single related value, like Host → Person. Render it inline: [Avatar] Sarah Williams.

Nested collection: multiple related records, like Visitor → Visits (3). Needs a different pattern.

The summary pattern

Instead of dumping a full table in the card:

Diagram
Visitor Card
────────────────────────────
John Smith
Acme Corporation
Checked In

Visits (3)                  +
────────────────────────────
Latest visit
02 Oct · Client Meeting

View all visits →

When a nested table IS justified

Use one when the child records matter to the task, users need to compare them, there are enough child fields to justify columns, and the relationship is central to the workflow.

Diagram
Expanded Visitor
────────────────────────────────

John Smith
Acme Corporation

Visit history (4)

Date       Host          Status
02 Oct     Sarah         Complete
18 Sep     David         Complete
02 Sep     Sarah         Cancelled

A relationship does not automatically justify a nested grid. "The data happens to be relational" is not a reason.

Classify the relationship

Text
One-to-one
One-to-many
Many-to-one
Many-to-many

Visitor → Host is one-to-one / many-to-one. Visitor → Visits and Company → Employees are one-to-many. Then the rendering rule follows:

Diagram
Single related entity      →  Inline
Small related collection   →  Summary
Large related collection   →  Nested table or detail surface
Complex related entity     →  Link to its detail view

The hard limit

Diagram
Card
 → Expanded
   → Nested table
      → Expanded row
         → Nested table

That's not a UI. That's the movie Inception. After one nested level, move to a dedicated detail surface, drawer or page. A company with 128 employees links to an Employees collection. It doesn't unroll 128 people inside a card.

[IMAGE 18 - Nested Data Decision] (DIAGRAM) AI generation prompt:


The Action System

Every record has actions. Not every action deserves a button. If every action gets a visible button, your table turns into a TV remote.

Level 1: Direct. Frequent, important, low-risk, immediately understandable, strongly tied to the record. View. Check in. Approve. Open. Show these directly.

Level 2: Secondary. Useful but not primary. Edit. Duplicate. Assign. Resend. A secondary button or overflow, depending on workflow.

Level 3: Destructive or rare. Delete. Archive. Revoke. Remove. Overflow, plus a confirmation when the consequence is significant.

Ask five questions:

Text
How often is this action used?
How important is it?
How risky is it?
How obvious is its meaning?
How much visual competition already exists?

Action

Frequency

Risk

Recommendation

View

High

Low

Direct

Check in

High

Low

Direct

Edit

Medium

Low

Secondary / direct, depending on workflow

Assign

Medium

Medium

Secondary

Duplicate

Low

Low

Overflow

Archive

Low

Medium

Overflow

Delete

Low

High

Overflow + confirmation

Placement

Sheet View: a dedicated trailing actions column. Use [⋯] when there are multiple secondary actions.

Text
| Name | Company | Status | Date | Actions |

Card View:

Diagram
Header
   ├── Status
   └── Overflow

Body
   └── Primary information

Footer
   └── Primary action

Two rules to live by. Don't duplicate the same action in three places. And don't make the entire card clickable if it contains many interactive controls. If opening the record is the primary action, make the identity or title the primary link and keep other controls separate. Otherwise users open records while aiming for a three-dot menu, then write angry emails.

[IMAGE 19 - Action Hierarchy] (FIGMA-STYLE with callouts) AI generation prompt:


Permissions, Record State and Actions

Here's where actions stop being a visual problem and become a logic problem. Availability can depend on role, permission, record state, ownership and workflow state. If the status is Checked In, the UI should not show Check in again. Instead Check out becomes available.

Text
User permissions
+
Record state
+
Action availability

For important workflows, document it. An action state matrix prevents the classic bug where the Sheet shows an action the Card doesn't:

Record State

View

Edit

Check In

Check Out

Delete

Invited

✓

✓

✓

—

✓

Checked In

✓

✓

—

✓

depends

Checked Out

✓

✓

—

—

depends

Cancelled

✓

limited

—

—

✓

Write it once. Use it everywhere. Argue about it once, in a meeting, instead of forever, in Slack.

[IMAGE 20 - Action State Matrix] (FIGMA-STYLE) AI generation prompt:


Selection and Bulk Actions

Bulk actions are not "single actions, but more." The toolbar should transform around them.

Nothing selected:

Text
Search | Filter | Add visitor

Records selected:

Text
12 selected
[Check in] [Assign] [Export] [⋯]

The toolbar changes because the user's intent changed.

Rules:

  • Only expose actions that are valid for all selected records.

  • With mixed states, either disable invalid actions with an explanation, apply the action only to eligible records with explicit feedback, or offer a clear mixed-state workflow.

  • Never silently fail. Silent failure is how trust dies.

And selection belongs to the workspace, not the renderer:

Diagram
Sheet
Select 5 records
   ↓
Switch to Card
   ↓
Same 5 records remain selected

[IMAGE 21 - Toolbar Transformation] (FIGMA-STYLE) AI generation prompt:


All three belong to the shared dataset. Both renderers consume the same sorted, filtered result.

Sorting is a small piece of state:

JSON
{
  "field": "checkIn",
  "direction": "descending"
}

Make the active sort obvious. Don't scatter sort affordances on every header if the table is already dense. A table with 14 little arrows looks like a cheap airport.

Filtering follows a hierarchy:

Text
Search
Primary filters
Advanced filters
Active filter chips
Clear filters

Filter types: Text, Select, Multi-select, Date, Date range, Number range, Status, Person, Company, Location, Boolean.

Always show what's active:

Text
Status: Checked In
Country: India
Date: Today

A filtered table with no visible filters is a haunted house. Users know something is hiding records, just not what.

[IMAGE 22 - Search, Filter, Sort Stack] (FIGMA-STYLE) AI generation prompt:


States: Empty, No Results, Loading, Error

Most teams design the happy path and improvise everything else at 5pm on release day.

Empty. No records exist.

Text
No visitors yet
Add your first visitor to get started.
[Add visitor]

No results. Records exist, but the current filters return none. This is not the same as empty. Please do not tell a user with 40,000 records they have "no visitors yet."

Text
No visitors match your filters.
[Clear filters]

Loading. Prefer structural loading (skeletons that mirror the real layout) where appropriate.

Error. Explain what happened, and what the user can do.

Text
We couldn't load visitors.
Try again or check your connection.

[Try again]

Add selected, expanded and disabled. Define all of them before the first real table is built.

[IMAGE 23 - The State Set] (FIGMA-STYLE) AI generation prompt:


Missing Data and Long Content

Two small things that wreck big products.

Missing data

Enterprise datasets are incomplete, and "missing" can mean very different things:

Text
Unknown
Not provided
Not applicable
Unavailable
Private
Pending

Do not collapse all of them into the same dash if they carry different meaning. "Private" and "Unknown" are not the same, and the user deserves to know whether data is hiding from them or simply doesn't exist. If they are semantically equivalent, establish one system-wide representation and stick to it. (Not four. We've seen four.)

Long content

Every field needs a truncation rule: single line, two lines, ellipsis, tooltip, expandable, or wrap.

  • Names: one or two lines

  • IDs: truncated but copyable

  • Notes: expandable

  • URLs: a human-readable label, not the raw URL

  • Long company names: ellipsis plus an accessible full value

Principle: never let one field destroy the entire layout.

[IMAGE 24 - Missing Data and Long Content] (FIGMA-STYLE) AI generation prompt:


Density

Density is a system-level setting, not a per-component hobby.

Text
Compact
Default
Comfortable

(Some systems add an Ultra Compact for the true power users.) Density affects row height, card padding, field gaps, cell padding, control sizes and vertical rhythm. The mistake is giving every component its own unrelated padding. If your table's "compact" is 36px and your card's "compact" is 11px of mystery padding, you don't have density, you have vibes.

Tie it to tokens (table.row.height, card.padding, record.summary.gap) so one setting reshapes everything. Power users living in the table all day will beg for compact. First-timers will love comfortable. Give both.

[IMAGE 25 - Density Comparison] (FIGMA-STYLE) AI generation prompt:


Responsive Behavior (Do Not Just Shrink the Table)

The laziest responsive strategy in history: shrink the desktop table until it fits. Define intentional transformations instead:

Diagram
Desktop
→ Sheet + Card available

Tablet
→ Sheet with fewer visible columns / Card

Mobile
→ Card-first

And priority earns its keep again:

Diagram
P0 → always
P1 → usually
P2 → conditional
P3 → expansion

The same model that decides what a card shows also decides which columns survive on a small screen. One system, many contexts.

[IMAGE 26 - Responsive Transformation] (FIGMA-STYLE) AI generation prompt:


Accessibility

Accessibility is not a QA item you remember at 6pm the night before launch. It's part of the architecture.

Sheet View needs: semantic table structure, keyboard navigation, visible focus, accessible sort controls, accessible selection, accessible status communication.

Card View needs: logical reading order, keyboard-accessible expansion, accessible action labels, proper focus management, no color-only information.

That last one deserves a spotlight.

Bad:

Text
Green = checked in

Better:

Text
● Checked In

Color should reinforce text, never be the only carrier of meaning.

[IMAGE 27 - Status Without Color] (FIGMA-STYLE) AI generation prompt:


Figma Architecture (Mirror the Code)

Organize Figma so its hierarchy mirrors the eventual development architecture. If design and code are structured differently, someone translates between them forever, and that someone is usually you at 11pm. (The 02 — Tokens page below is the same token architecture from our design tokens guide, just consumed by data components.)

Text
00 — Documentation
01 — Foundations
02 — Tokens
03 — Data Primitives
04 — Field Components
05 — Table Components
06 — Card Components
07 — Actions
08 — View Controls
09 — States
10 — Patterns
11 — Real Tables
12 — Playground

Component families:

Text
Data / Text, Number, Date, DateTime, Person, Company, Country, Status, Tag, Link, Actions

Table / Container, Shell, Toolbar, Header, Header Cell, Row, Cell, Selection, Expansion, Footer, Pagination

Record Card / Header, Identity, Summary, Field, Details, Actions, Expansion

Properties on a Record Card:

Text
State:    Default / Hover / Focused / Selected / Expanded / Disabled / Loading
Density:  Compact / Default / Comfortable
Status:   None / Success / Warning / Error / Info / Neutral
Show Avatar:  True / False
Show Status:  True / False
Expanded:     True / False

And a warning: do not create a property for every possible field. Fields should be composable through nested components. Otherwise you get a component with 47 toggles nobody dares touch, like the fuse box in a rental apartment.

Most important: page 11, "Real Tables," holds instances and configurations, not bespoke component families.

[IMAGE 28 - Figma Page Structure] (FIGMA UI) AI generation prompt:


Development Architecture

The engineering model is the same idea in code:

Diagram
DataView
│
├── DataSource
│
├── QueryState
│   ├── Search
│   ├── Filters
│   ├── Sort
│   ├── Pagination
│   └── Selection
│
└── Renderer
    ├── SheetRenderer
    └── CardRenderer

And field rendering:

Diagram
FieldDefinition
      ↓
FieldType
      ↓
FieldRenderer

That's how one enterprise data system supports many resources without each getting a custom implementation.

[IMAGE 29 - Component Tree] (CODE UI) AI generation prompt:


Configuration Schema and the Data Contract

This is why all of it matters in practice: a real table becomes a configuration.

JSON
{
  "resource": "visitors",
  "view": {
    "default": "sheet",
    "available": ["sheet", "card"]
  },
  "fields": [
    {
      "key": "name",
      "type": "person",
      "priority": "P0",
      "sortable": true,
      "filterable": true,
      "searchable": true
    },
    {
      "key": "company",
      "type": "company",
      "priority": "P1"
    },
    {
      "key": "status",
      "type": "status",
      "priority": "P1",
      "filterable": true
    },
    {
      "key": "country",
      "type": "country",
      "priority": "P2"
    }
  ]
}

The same schema drives both views. Add a field to the config, and it appears in the Sheet, the Card and the expanded details, with the right priority logic. Nobody opens Figma. Nobody files "Please add one more column :)".

The contract

Every real table also gets a formal Data Display Contract connecting Product, Design, Development and QA. Think of it as the table's employment agreement, with clauses.

Table / resource level:

Text
Purpose
Primary user
Primary task
Record type
Field count
Primary identifier
Primary status
Primary actions

Field level:

Property

Definition

Field

Name of the field

Type

Rendering type

Priority

P0 / P1 / P2 / P3

Renderer

Visual treatment

Interactive

Can it be interacted with?

Sortable

Supports sorting

Filterable

Supports filtering

Searchable

Supports search

Sheet visible

Visible in Sheet

Card collapsed

Visible in collapsed card

Card expanded

Visible in expanded card

The contract stops a table from becoming an undocumented collection of visual decisions that only one person remembers, usually the one who just resigned.

[IMAGE 30 - The Data Contract] (FIGMA-STYLE) AI generation prompt:


Performance (When 20 Records Become 20,000)

Your table will be tested with 20 records in the demo and 2 million in production. Real life enjoys irony. Assume it from day one:

Text
Server-side filtering
Server-side sorting
Pagination
Virtualization
Lazy loading
Debounced search
Incremental loading

The golden rule: do not assume the entire dataset exists in the browser. If your plan is "load everything and filter it in JavaScript," your plan has an expiry date.

Cards need extra care. They contain richer components than rows, so they cost more to render. Don't render off-screen records nobody can see.

[IMAGE 31 - Scale Strategy] (DIAGRAM) AI generation prompt:


Governance: How the System Evolves

A data display system dies the same way a token system does: everyone adds "just one more" custom thing. Somebody needs a new field type. Somebody invents a seventh status treatment. Somebody builds "just a quick card" for one screen.

Define up front:

  • who can add a new field type or cell

  • who approves a new renderer

  • how priority rules change

  • how breaking changes, deprecation and migration are handled

  • where the contract for each table lives

A typical lifecycle:

Diagram
Request
   ↓
Proposal
   ↓
Review
   ↓
Approved
   ↓
Implementation
   ↓
Documentation
   ↓
Release

Yes, it looks like a corporate expense report. Chaos hates paperwork.

[IMAGE 32 - Governance Lifecycle] (DIAGRAM) AI generation prompt:


Real-World Examples

Visitor management

A visitor record can contain: Name, Company, Country, Email, Phone, Host, Visit Type, Purpose, Location, Date, Check-in, Check-out, Status, Created, Created By, Updated, Visitor ID, Approval, Notes. Nineteen fields.

Sheet View optimizes for scanning: Name | Company | Host | Date | Status | ...

Card View, collapsed:

Diagram
┌──────────────────────────────────────────────┐
│ [Avatar] John Smith             ● Checked In │
│          Acme Corporation                    │
│                                              │
│ Host       Sarah Williams                    │
│ Visit      Client Meeting                    │
│ Date       02 Oct · 10:30 AM                 │
│                                              │
│ + 12 more details                    ↓       │
└──────────────────────────────────────────────┘

Card View, expanded:

Diagram
┌──────────────────────────────────────────────┐
│ Identity and summary remain visible          │
│                                              │
│ Country       🇮🇳 India                      │
│ Email         john@acme.com                  │
│ Phone         +91 XXXXX XXXXX                │
│ Purpose       Client Meeting                 │
│ Location      Building A                     │
│ Check-in      09:42 AM                       │
│ Check-out     —                              │
│ Created by    Sarah Williams                 │
│ Visitor ID    VIS-10482                      │
│ Approval      Approved                       │
│ Notes         ...                            │
│                                              │
│                              Show less ↑     │
└──────────────────────────────────────────────┘

The four-field dataset

Another table only has Device, Location, Status and Last Seen. Don't force the visitor-card density:

Diagram
┌──────────────────────────────────────────────┐
│ Device 01                         ● Online   │
│ Building A · Floor 2                         │
│ Last seen 10:42 AM                           │
└──────────────────────────────────────────────┘

No expansion. Nothing hidden. Done.

Company with nested employees

Diagram
┌──────────────────────────────────────────────┐
│ Acme Corporation                             │
│ 128 employees                    ● Active    │
│                                              │
│ Primary contact: Sarah Williams              │
│ Location: London                             │
│                                              │
│ Employees (128)                     View →   │
└──────────────────────────────────────────────┘

Clicking Employees should usually open a dedicated collection view, not unroll 128 people inside the card.

Record actions for a visitor

  • Primary: View

  • Contextual: Check in

  • Secondary: Edit, Assign host, Resend invite

  • Rare / destructive: Cancel visit, Delete

Text
[View details]                         [⋯]

Overflow: Edit, Assign host, Resend invitation, Cancel visit, Delete. The exact actions depend on permissions and record state.

[IMAGE 33 - Three Resources, One System] (FIGMA-STYLE) AI generation prompt:


Anti-Patterns: The Hall of Shame

Every table gets a custom card.

Text
20 tables → 20 cards → 20 interaction models

Instead:

Text
20 schemas → 1 Record Card System → 20 configurations

Forcing exactly four fields. Fails with two fields. Fails with twenty-five.

Putting every field in the collapsed card. Congratulations, you made a form that scrolls.

Turning Card View into a Detail Page. Destroys scanning, makes collection views heavy.

Copying a reference literally. Zoho (or anyone) is a fine interaction reference. Your architecture should still be specific to your system.

Icons everywhere. Richness without meaning is noise with a stroke width.

Separate query logic per view. Search, filter, sort and selection must be shared.

Infinite nested expansion. One level, then a nested table, drawer or detail surface.


The Checklists

Design QA

Data

  • Are all fields classified?

  • Are field types correct?

  • Are priorities defined?

  • Are missing states defined?

Sheet

  • Can users compare records?

  • Are columns aligned?

  • Are actions predictable?

  • Is density appropriate?

Card

  • Is identity immediately recognizable?

  • Are primary fields visible?

  • Is expansion meaningful?

  • Is rich formatting semantic?

Actions

  • Are primary actions obvious?

  • Are rare actions hidden?

  • Are destructive actions protected?

  • Are permissions respected?

Nested data

  • Are relationships understandable?

  • Is nested data necessary?

  • Is nesting limited?

States

  • Loading? Empty? No results? Error? Disabled? Selected? Expanded?

Accessibility

  • Keyboard? Focus? Screen reader? Color-independent status?

Performance

  • Pagination? Virtualization? Server-side operations? Lazy rendering?

Engineering QA

Text
□ Same schema supports Sheet + Card
□ Search state is shared
□ Filter state is shared
□ Sort state is shared
□ Selection is shared
□ Pagination is shared
□ Field renderers are reusable
□ Permissions affect actions
□ Record state affects actions
□ Nested data has explicit rules
□ Loading states exist
□ Error states exist
□ Empty states exist
□ Accessibility is implemented
□ Long values are handled
□ Missing values are handled
□ Large datasets are supported
□ View switching preserves context

Tick most of these, and you're no longer maintaining a pile of tables. You're running real data-display infrastructure.


Do not start by designing every real table. That's how you end up with nineteen tables and zero system. Build in this order:

Text
Phase 1 — Foundations
Colors, Typography, Spacing, Radius, Borders, Icons, Elevation

Phase 2 — Data primitives
Text, Number, Date, Person, Company, Country, Status, Tag, Link, Actions

Phase 3 — Field system
Field type, Field priority, Field renderer, Field states

Phase 4 — Sheet system
Table, Header, Cell, Row, Selection, Expansion, Toolbar, Filters, Pagination

Phase 5 — Card system
Record Card, Identity, Summary, Metadata, Details, Actions, Expansion

Phase 6 — View system
Sheet, Card, View switcher, Density, Configuration

Phase 7 — Complex data
Nested records, Nested collections, Related entities, Attachments, Audit information

Phase 8 — Real tables
Visitors, Visits, Employees, Hosts, Companies, Devices...

Only in Phase 8 do you build the "real" tables. By then they're configurations and demonstrations of the system, not fresh inventions.

[IMAGE 34 - Build Sequence] (DIAGRAM) AI generation prompt:


Where This Fits in the Series

This ebook is Tutorial 02 in the Enterprise Product Systems series. Tutorial 01, How to Build an Enterprise-Grade Design Token System, came first, because everything here runs on tokens. And this one quietly plants the seeds for what comes next:

Diagram
Tokens (done)
   ↓
Data Display System (you are here)
   ↓
Forms → Search & Filtering → Actions → Permissions → Workflows → Record Detail → ...

Everything we touched on briefly here (actions, permissions, bulk operations, states, density, nested records, audit) gets its own full tutorial. Think of this ebook as the foundation the rest of the enterprise product is going to stand on.

[IMAGE 35 - The Series Map] (DIAGRAM) AI generation prompt:


The Mental Model to Remember

If you forget everything else, remember this flow:

Diagram
ENTERPRISE DATA
                              │
                              ▼
                         DATA MODEL
                              │
                              ▼
                            SCHEMA
                              │
              ┌───────────────┼───────────────┐
              ▼               ▼               ▼
          FIELD TYPE       PRIORITY        BEHAVIOR
              │               │               │
              └───────────────┼───────────────┘
                              ▼
                       VIEW CONFIGURATION
                              │
                 ┌────────────┴────────────┐
                 ▼                         ▼
           SHEET RENDERER             CARD RENDERER
                 │                         │
           Comparison                 Recognition
           Dense                      Rich
           Structured                 Hierarchical
                 │                         │
                 └────────────┬────────────┘
                              ▼
                         USER ACTIONS

And the one-liner philosophy, for the office fridge:

A table optimizes for comparison. A card optimizes for recognition. Expansion optimizes for progressive disclosure. A detail surface optimizes for investigation. Tokens optimize for consistency. Configuration optimizes for scale. A shared data model makes all of them work together.

[IMAGE 36 - Mental Model Hero Diagram] (DIAGRAM, closing hero, slightly larger and more refined) AI generation prompt:


Final Thought

A table component answers one question: how do we show rows?

A data display system answers a much bigger one: how do we turn complex data into understandable interfaces, across every resource, every screen size and every user, without rebuilding it all each time?

The strongest enterprise data systems are not collections of beautiful components. They are rules. Rules that let a designer or developer take any enterprise record and answer:

Text
What is it?
What matters?
What should be visible?
What should be hidden?
How should it render?
What can the user do?
What happens when it expands?
What happens when data is missing?
What happens when there are 100,000 records?

If your system answers those questions consistently, you have an enterprise-grade data display system. And somewhere, a very tired developer who used to maintain nineteen different tables is quietly crying tears of joy into their coffee.


Written (and mildly overcaffeinated) by Vishal Anand (Founder) and Syed Musuddiq (Head of Design) at UI Pirate.

Working on something similar?

We design and build products like this for SaaS teams.

Tell us what you are building and we will tell you honestly how we would approach it — no deck, no obligation.

More to Read

All articles →