Data

Tables for dense data views

Tables align columns for scannable datasets—admin consoles, billing histories, and analytics exports. Compose them in React pages and Next.js server-rendered listings with stable column widths.

Recent deploys
ServiceRegionLatency
APIiad42ms
Workersfra28ms

Examples

Bordered appearance with sticky headers and composable sorting for data views.


Click a sortable header to cycle ascending, descending, and none. The active state stays outside the Table primitive.

InvoiceCustomerStatusAmount
INV-1048Acme LabsPaid$1,840
INV-1049Northstar Co.Pending$960
INV-1050Orbit WorksOverdue$2,260
INV-1051Pixel ForgePaid$1,280

IdName
1Alpha
2Beta
3Gamma

Table variants examples

Density, row style, and sticky header with scrollable preview. Each code view starts with Variant: naming the row.


Appearance: default | Size: md | Sticky Header: false

NameRoleEmailAge
User 1Adminuser1a@example.com20
User 2Useruser2a@example.com25
User 3Useruser3a@example.com30
User 4Useruser4a@example.com30

Appearance: striped | Size: md | Sticky Header: false

NameRoleEmailAge
User 1Adminuser1a@example.com20
User 2Useruser2a@example.com25
User 3Useruser3a@example.com30
User 4Useruser4a@example.com30

Appearance: bordered | Size: md | Sticky Header: false

NameRoleEmailAge
User 1Adminuser1a@example.com20
User 2Useruser2a@example.com25
User 3Useruser3a@example.com30
User 4Useruser4a@example.com30

Appearance: ghost | Size: md | Sticky Header: false

NameRoleEmailAge
User 1Adminuser1a@example.com20
User 2Useruser2a@example.com25
User 3Useruser3a@example.com30
User 4Useruser4a@example.com30

Appearance: sky | Size: md | Sticky Header: false

NameRoleEmailAge
User 1Adminuser1a@example.com20
User 2Useruser2a@example.com25
User 3Useruser3a@example.com30
User 4Useruser4a@example.com30

Appearance: rose | Size: md | Sticky Header: false

NameRoleEmailAge
User 1Adminuser1a@example.com20
User 2Useruser2a@example.com25
User 3Useruser3a@example.com30
User 4Useruser4a@example.com30

Appearance: purple | Size: md | Sticky Header: false

NameRoleEmailAge
User 1Adminuser1a@example.com20
User 2Useruser2a@example.com25
User 3Useruser3a@example.com30
User 4Useruser4a@example.com30

Appearance: pink | Size: md | Sticky Header: false

NameRoleEmailAge
User 1Adminuser1a@example.com20
User 2Useruser2a@example.com25
User 3Useruser3a@example.com30
User 4Useruser4a@example.com30

Appearance: orange | Size: md | Sticky Header: false

NameRoleEmailAge
User 1Adminuser1a@example.com20
User 2Useruser2a@example.com25
User 3Useruser3a@example.com30
User 4Useruser4a@example.com30

Appearance: yellow | Size: md | Sticky Header: false

NameRoleEmailAge
User 1Adminuser1a@example.com20
User 2Useruser2a@example.com25
User 3Useruser3a@example.com30
User 4Useruser4a@example.com30

Appearance: teal | Size: md | Sticky Header: false

NameRoleEmailAge
User 1Adminuser1a@example.com20
User 2Useruser2a@example.com25
User 3Useruser3a@example.com30
User 4Useruser4a@example.com30

Appearance: indigo | Size: md | Sticky Header: false

NameRoleEmailAge
User 1Adminuser1a@example.com20
User 2Useruser2a@example.com25
User 3Useruser3a@example.com30
User 4Useruser4a@example.com30

Appearance: emerald | Size: md | Sticky Header: false

NameRoleEmailAge
User 1Adminuser1a@example.com20
User 2Useruser2a@example.com25
User 3Useruser3a@example.com30
User 4Useruser4a@example.com30

Appearance: gray | Size: md | Sticky Header: false

NameRoleEmailAge
User 1Adminuser1a@example.com20
User 2Useruser2a@example.com25
User 3Useruser3a@example.com30
User 4Useruser4a@example.com30

Appearance: amber | Size: md | Sticky Header: false

NameRoleEmailAge
User 1Adminuser1a@example.com20
User 2Useruser2a@example.com25
User 3Useruser3a@example.com30
User 4Useruser4a@example.com30

Appearance: violet | Size: md | Sticky Header: false

NameRoleEmailAge
User 1Adminuser1a@example.com20
User 2Useruser2a@example.com25
User 3Useruser3a@example.com30
User 4Useruser4a@example.com30

Appearance: striped | Size: sm | Sticky Header: false

NameRoleEmailAge
User 1Adminuser1a@example.com20
User 2Useruser2a@example.com25
User 3Useruser3a@example.com30
User 4Useruser4a@example.com30

Appearance: striped | Size: md | Sticky Header: false

NameRoleEmailAge
User 1Adminuser1a@example.com20
User 2Useruser2a@example.com25
User 3Useruser3a@example.com30
User 4Useruser4a@example.com30

Appearance: striped | Size: lg | Sticky Header: false

NameRoleEmailAge
User 1Adminuser1a@example.com20
User 2Useruser2a@example.com25
User 3Useruser3a@example.com30
User 4Useruser4a@example.com30

Appearance: striped | Size: md | Sticky Header: false

NameRoleEmailAge
User 1Adminuser1a@example.com20
User 2Useruser2a@example.com25
User 3Useruser3a@example.com30
User 4Useruser4a@example.com30

Appearance: striped | Size: md | Sticky Header: false

NameRoleEmailAge
User 1Adminuser1a@example.com20
User 2Useruser2a@example.com25
User 3Useruser3a@example.com30
User 4Useruser4a@example.com30

Appearance: striped | Size: md | Sticky Header: false

NameRoleEmailAge
User 1Adminuser1a@example.com20
User 2Useruser2a@example.com25
User 3Useruser3a@example.com30
User 4Useruser4a@example.com30

Appearance: bordered | Size: md | Sticky Header: true

NameRoleEmailAge
User 1Adminuser1a@example.com20
User 2Useruser2a@example.com25
User 3Useruser3a@example.com30
User 4Useruser4a@example.com30

Appearance: bordered | Size: md | Sticky Header: false

NameRoleEmailAge
User 1Adminuser1a@example.com20
User 2Useruser2a@example.com25
User 3Useruser3a@example.com30
User 4Useruser4a@example.com30
CSS variable overrides

Table CSS variables

Override these table variables on :root, a theme selector, or a component wrapper.

129 variables

Pattern: --zui-<component>-<slot?>-<variant?>-<property>-<state?>-dark?

:root {
  --zui-table-fg: oklch(37.2% 0.044 257.287);
  --zui-table-bordered-border: #0000001a;
  --zui-table-sky-border: oklch(44.3% 0.11 240.79);
  --zui-table-rose-border: oklch(45.5% 0.188 13.697);
  --zui-table-purple-border: oklch(43.8% 0.218 303.724);
  --zui-table-pink-border: oklch(45.9% 0.187 3.815);
  --zui-table-orange-border: oklch(47% 0.157 37.304);
  --zui-table-yellow-border: oklch(47.6% 0.114 61.907);
  --zui-table-teal-border: oklch(43.7% 0.078 188.216);
  --zui-table-indigo-border: oklch(39.8% 0.195 277.366);
  --zui-table-emerald-border: oklch(43.2% 0.095 166.913);
  --zui-table-gray-border: oklch(27.8% 0.033 256.848);
  --zui-table-amber-border: oklch(47.3% 0.137 46.201);
  --zui-table-violet-border: oklch(43.2% 0.232 292.759);
  --zui-table-row-border: #0000000d;
  --zui-table-row-bg-selected: #0000000f;
  --zui-table-row-striped-bg: #00000008;
  --zui-table-row-ghost-border: transparent;
  --zui-table-row-ghost-bg-hover: #00000008;
  --zui-table-row-sky-border: oklch(44.3% 0.11 240.79);
  --zui-table-row-sky-bg-hover: oklch(44.3% 0.11 240.79);
  --zui-table-row-sky-fg-hover: oklch(95.1% 0.026 236.824);
  --zui-table-row-rose-border: oklch(45.5% 0.188 13.697);
  --zui-table-row-rose-bg-hover: oklch(64.5% 0.246 16.439);
  --zui-table-row-rose-fg-hover: oklch(94.1% 0.03 12.58);
  --zui-table-row-purple-border: oklch(43.8% 0.218 303.724);
  --zui-table-row-purple-bg-hover: oklch(62.7% 0.265 303.9);
  --zui-table-row-purple-fg-hover: oklch(94.6% 0.033 307.174);
  --zui-table-row-pink-border: oklch(45.9% 0.187 3.815);
  --zui-table-row-pink-bg-hover: oklch(65.6% 0.241 354.308);
  --zui-table-row-pink-fg-hover: oklch(94.8% 0.028 342.258);
  --zui-table-row-orange-border: oklch(47% 0.157 37.304);
  --zui-table-row-orange-bg-hover: oklch(70.5% 0.213 47.604);
  --zui-table-row-orange-fg-hover: oklch(95.4% 0.038 75.164);
  --zui-table-row-yellow-border: oklch(47.6% 0.114 61.907);
  --zui-table-row-yellow-bg-hover: oklch(79.5% 0.184 86.047);
  --zui-table-row-yellow-fg-hover: oklch(97.3% 0.071 103.193);
  --zui-table-row-teal-border: oklch(43.7% 0.078 188.216);
  --zui-table-row-teal-bg-hover: oklch(70.4% 0.14 182.503);
  --zui-table-row-teal-fg-hover: oklch(95.3% 0.051 180.801);
  --zui-table-row-indigo-border: oklch(39.8% 0.195 277.366);
  --zui-table-row-indigo-bg-hover: oklch(58.5% 0.233 277.117);
  --zui-table-row-indigo-fg-hover: oklch(93% 0.034 272.788);
  --zui-table-row-emerald-border: oklch(43.2% 0.095 166.913);
  --zui-table-row-emerald-bg-hover: oklch(69.6% 0.17 162.48);
  --zui-table-row-emerald-fg-hover: oklch(95% 0.052 163.051);
  --zui-table-row-gray-border: oklch(27.8% 0.033 256.848);
  --zui-table-row-gray-bg-hover: oklch(55.1% 0.027 264.364);
  --zui-table-row-gray-fg-hover: oklch(96.7% 0.003 264.542);
  --zui-table-row-amber-border: oklch(47.3% 0.137 46.201);
  --zui-table-row-amber-bg-hover: oklch(76.9% 0.188 70.08);
  --zui-table-row-amber-fg-hover: oklch(96.2% 0.059 95.617);
  --zui-table-row-violet-border: oklch(43.2% 0.232 292.759);
  --zui-table-row-violet-bg-hover: oklch(60.6% 0.25 292.717);
  --zui-table-row-violet-fg-hover: oklch(94.3% 0.029 294.588);
  --zui-table-cell-default-border: #0000001a;
  --zui-table-cell-striped-border: #0000001a;
  --zui-table-cell-bordered-border: #0000001a;
  --zui-table-cell-ghost-border: #0000001a;
  --zui-table-cell-sky-border: oklch(44.3% 0.11 240.79);
  --zui-table-cell-rose-border: oklch(45.5% 0.188 13.697);
  --zui-table-cell-purple-border: oklch(43.8% 0.218 303.724);
  --zui-table-cell-pink-border: oklch(45.9% 0.187 3.815);
  --zui-table-cell-orange-border: oklch(47% 0.157 37.304);
  --zui-table-cell-yellow-border: oklch(47.6% 0.114 61.907);
  --zui-table-cell-teal-border: oklch(43.7% 0.078 188.216);
  --zui-table-cell-indigo-border: oklch(39.8% 0.195 277.366);
  --zui-table-cell-emerald-border: oklch(43.2% 0.095 166.913);
  --zui-table-cell-gray-border: oklch(27.8% 0.033 256.848);
  --zui-table-cell-amber-border: oklch(47.3% 0.137 46.201);
  --zui-table-cell-violet-border: oklch(43.2% 0.232 292.759);
}

/* Dark theme variables follow the same names with -dark appended. */
.dark {
  --zui-table-fg-dark: oklch(92.9% 0.013 255.508);
  --zui-table-bordered-border-dark: #ffffff1a;
  /* ...same variables with -dark at the end */
}

What it does

Use semantic table elements for accessibility by default.

Sticky headers help long scroll regions remain navigable.

Composition and API

Prefer compound subcomponents instead of one oversized prop bag. Export a small, documented API for your design system.

Provide captions or aria-labels when the table’s purpose is not obvious from surrounding headings.

Common use cases

  • Operational dashboards with sortable columns.
  • Audit logs with monospace numerics.
  • Pricing matrices with tier comparisons.
  • Inventory views with row-level actions.

Accessibility

Keyboard order, focus rings, and ARIA attributes should match production usage. Test with your supported browsers and assistive technologies when semantics are non-trivial.

This preview page exposes a single h1 in the hero for a clean outline.

Next.js integration notes

Colocate examples under the App Router, keep server and client boundaries explicit, and avoid pulling interactive overlays into unexpected server layouts.

Set NEXT_PUBLIC_SITE_URL so canonical and Open Graph URLs resolve on deploy.

FAQ

Does the Table component work with Next.js App Router?

Yes. Import it like any other React component; keep interactive subtrees in client components when you need hooks or browser APIs, and leave static structure in server components where possible.

Can I customize table with Tailwind CSS?

Zentauri UI exposes class-friendly variants and slots so you can extend styles with Tailwind utilities without fighting inline styles.

Is this Table implementation accessible by default?

Primitives follow sensible defaults, but accessibility depends on how you label controls, manage focus, and wire keyboard handlers in your app. Validate critical flows with keyboard-only use and screen readers.