Skip to main contentSkip to docs navigation

Static Table

A plain, server-renderable table with Table's styling and no client JavaScript, for listings that don't need sorting, selection, or grid navigation.

Overview

StaticTable renders a <table> with the same styling props as Table and none of its behavior. You write the rows as ordinary <thead>/<tbody>/<tfoot>/<tr>/<th>/<td> markup and it renders them as-is. It has no hooks and no state, so it ships no JavaScript to the browser.

Reach for it when a page only needs consistent table styling around server-rendered rows: a listing of links, a history log, a table with server-action forms in its cells. Reach for Table instead when you need sorting, row selection, or arrow-key grid navigation.

Most active members
NameProfileTopics
Mark Otto@mdo42
Jacob Thornton@fat17
Larry Bird@twitter8
JSX
import { StaticTable } from '@chassis-ui/react/static-table'

const users = [
  { id: 1, name: 'Mark Otto', username: 'mdo', topics: 42 },
  { id: 2, name: 'Jacob Thornton', username: 'fat', topics: 17 },
  { id: 3, name: 'Larry Bird', username: 'twitter', topics: 8 }
]

export const Example = () => (
  <StaticTable caption="Most active members">
    <thead>
      <tr>
        <th scope="col">Name</th>
        <th scope="col">Profile</th>
        <th scope="col">Topics</th>
      </tr>
    </thead>
    <tbody>
      {users.map((user) => (
        <tr key={user.id} id={user.username}>
          <td>{user.name}</td>
          <td>
            <a href={`#${user.username}`}>@{user.username}</a>
          </td>
          <td>{user.topics}</td>
        </tr>
      ))}
    </tbody>
  </StaticTable>
)

In Server Components

Import it from its own entry point, @chassis-ui/react/static-table. That's the one entry point in the package without a 'use client' directive, so it renders as a genuine Server Component: no client boundary, no hydration, and no JavaScript for it in the page's bundle.

TSX
// app/members/page.tsx — a Server Component
import Link from 'next/link'
import { StaticTable } from '@chassis-ui/react/static-table'

export default async function Members() {
  const members = await getMembers()
  return (
    <StaticTable hover striped>
      <thead>
        <tr>
          <th scope="col">Name</th>
          <th scope="col">Topics</th>
        </tr>
      </thead>
      <tbody>
        {members.map((member) => (
          <tr key={member.id}>
            <td>
              <Link href={`/u/${member.username}`}>{member.name}</Link>
            </td>
            <td>{member.topics}</td>
          </tr>
        ))}
      </tbody>
    </StaticTable>
  )
}

StaticTable is exported from the root @chassis-ui/react entry too, for use inside Client Components. Imported from there into a Server Component, it still works, but it becomes a client component, because the root entry is a client boundary.

Variants

bordered, borderless, hover, sm, striped, color, and align apply the same classes they do on Table.

NameRole
Mark OttoEngineer
Jacob ThorntonDesigner
Larry BirdEngineer
3 members
JSX
import { StaticTable } from '@chassis-ui/react/static-table'

const rows = [
  { id: 1, name: 'Mark Otto', role: 'Engineer' },
  { id: 2, name: 'Jacob Thornton', role: 'Designer' },
  { id: 3, name: 'Larry Bird', role: 'Engineer' }
]

export const Example = () => (
  <StaticTable aria-label="Team" bordered hover sm striped>
    <thead>
      <tr>
        <th scope="col">Name</th>
        <th scope="col">Role</th>
      </tr>
    </thead>
    <tbody>
      {rows.map((row) => (
        <tr key={row.id}>
          <td>{row.name}</td>
          <td>{row.role}</td>
        </tr>
      ))}
    </tbody>
    <tfoot>
      <tr>
        <td colSpan={2}>3 members</td>
      </tr>
    </tfoot>
  </StaticTable>
)

Responsive and stacked tables

responsive wraps the table in a horizontally scrolling container: true for all viewports, or a breakpoint name to scroll only below it. stacked converts rows into label/value blocks below a container width.

Table derives each stacked label from its column header. A static table has no column data to read, so give each <td> a data-cell attribute with the label to show. Resize the panel below to see it switch.

NameRoleEmailStatus
Alice MartinAdminalice@example.comActive
Bob ChenEditorbob@example.comActive
Carol WhiteViewercarol@example.comInactive
JSX
import { StaticTable } from '@chassis-ui/react/static-table'

const rows = [
  { id: 1, name: 'Alice Martin', role: 'Admin', email: 'alice@example.com', status: 'Active' },
  { id: 2, name: 'Bob Chen', role: 'Editor', email: 'bob@example.com', status: 'Active' },
  { id: 3, name: 'Carol White', role: 'Viewer', email: 'carol@example.com', status: 'Inactive' }
]

export const Example = () => (
  <StaticTable aria-label="Users" stacked="md">
    <thead>
      <tr>
        <th scope="col">Name</th>
        <th scope="col">Role</th>
        <th scope="col">Email</th>
        <th scope="col">Status</th>
      </tr>
    </thead>
    <tbody>
      {rows.map((row) => (
        <tr key={row.id}>
          <td data-cell="Name">{row.name}</td>
          <td data-cell="Role">{row.role}</td>
          <td data-cell="Email">{row.email}</td>
          <td data-cell="Status">{row.status}</td>
        </tr>
      ))}
    </tbody>
  </StaticTable>
)

Accessibility

Since StaticTable renders your markup unchanged, table semantics are yours to write: add scope="col" (or scope="row") to header cells, and give the table an accessible name with caption, aria-label, or aria-labelledby.

API

StaticTable

PropTypeDefault
align?"bottom" | "middle" | "top"—
Set the vertical alignment of cell content.
bordered?boolean—
Add borders on all sides of the table and cells.
borderless?boolean—
Remove borders on all sides of the table and cells.
caption?ReactNode—
Content shown above the table, functioning as a heading for it.
children?ReactNode—
The table's own markup — plain <thead>/<tbody>/<tfoot>/<tr>/<th>/<td> elements, rendered as-is.
className?string—
A string of all className you want applied to the component.
color?ContextColor—
Sets the color of the component.
hover?boolean—
Enable a hover state on table rows.
responsive?boolean | Breakpoint—
Make any table responsive across all viewports or pick a maximum breakpoint.
sm?boolean—
Make table more compact by cutting all cell padding.
stacked?boolean | Breakpoint—
Convert rows into stacked label/value blocks below a container width, for tables with too many columns to read comfortably even with horizontal scrolling. true always stacks; a breakpoint name stacks only below it. Implies responsive when responsive isn't set separately. Unlike Table, which derives each cell's label from its column header, a static table has no collection to read labels from — give each <td> a data-cell attribute with the label to show before its value.
striped?boolean—
Add zebra-striping to table rows.