Matrix Table
A fixed assignment grid for members×roles patterns. Sticky-left identity column, centered boolean cells, and multi-line column headers. No list machinery — copy it into your project and own it completely.
Basic
| Member | Admin3 | Editor8 | Viewer14 | Billing2 |
|---|---|---|---|---|
| Ada Lovelace | — | — | ||
| Grace Hopper | — | — | ||
| Margaret Hamilton | — | — | — | |
| Dennis Ritchie | ||||
| Linus Torvalds | — | — | — | — |
| Seat allowance | 5 | Unlimited | — |
When to use
Use MatrixTable when the shape of your data is a matrix — rows are identities (users, members, items) and columns are boolean attributes (roles, permissions, features). The component is intentionally minimal: it has no selection column, no sort buttons, and no pagination footer.
For sortable, paginated lists of records, use DataTable instead.
renderIdentity slot
The renderIdentity prop receives each row's identity value and renders it in the sticky-left cell. This slot is intentionally open-ended — pass an avatar+name compound, a plain string, or any identity representation your design requires.
<MatrixTable
columns={columns}
rows={rows}
renderIdentity={(member) => (
<div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem' }}>
<img src={member.avatarUrl} alt="" style={{ width: 24, height: 24, borderRadius: '50%' }} />
<span>{member.name}</span>
</div>
)}
/>activeColumns
Each row's activeColumns accepts either a Set<string> or a plain string[]. Column ids not present in activeColumns render as empty/inactive indicators.
// Both forms are equivalent
{ id: '1', identity: member, activeColumns: new Set(['admin', 'editor']) }
{ id: '1', identity: member, activeColumns: ['admin', 'editor'] }String cell values
Most cells are boolean (does this row have this column? yes/no), but feature-comparison
matrices often need a value cell — "50GB", "Unlimited". Set per-column values with the
optional cells map, which accepts string | boolean (MatrixCellValue):
true→ the active checkmark indicatorfalse→ the empty/inactive indicatorstring→ plain text in the standard cell style
// Feature row: free plan has no value, pro is included, enterprise gets a string value.
{
id: 'storage',
identity: { name: 'Storage' },
activeColumns: [],
cells: { free: false, pro: true, enterprise: '50GB' },
}A column present in cells takes precedence over its activeColumns membership; columns
absent from cells fall back to the activeColumns boolean model. String cells expose
role="cell" with an accessible name of "<column>: <value>"; boolean cells keep the
existing assigned / not assigned aria-label.
Installation
npx visor add matrix-tableThis copies two files into your project:
components/ui/matrix-table/matrix-table.tsx— the componentcomponents/ui/matrix-table/matrix-table.module.css— the styles
import { MatrixTable, type MatrixColumn, type MatrixRow } from '@/components/ui/matrix-table/matrix-table';API Reference
No props data available for “”.
Kbd
Tiny primitive for rendering keyboard shortcuts using the semantic <kbd> element. Supports a single key via children or a multi-key sequence via the keys prop with a customizable separator. Built with CSS Modules — copy it into your project and own it completely.
Page Header
Admin page header compound with eyebrow, title, description, breadcrumb, and actions slots. Built with CSS Modules and container queries — copy it into your project and own it completely.