VisorVisor
ComponentsAdmin

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

MemberAdmin3Editor8Viewer14Billing2
Ada Lovelace
Grace Hopper
Margaret Hamilton
Dennis Ritchie
Linus Torvalds
Seat allowance5Unlimited

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 indicator
  • false → the empty/inactive indicator
  • string → 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-table

This copies two files into your project:

  • components/ui/matrix-table/matrix-table.tsx — the component
  • components/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 “”.