|Kit

Table

Dumb, styled semantic table on a bordered surface. Pure Server Component with namespaced parts — the primitive DataTable renders through.

Table is a thin, styled wrapper over a semantic <table>, sitting inside a bordered, rounded surface. It's a pure Server Component — no state, no "use client" — so it drops straight into an RSC tree or an MDX page.

You compose it from namespaced parts: an optional non-table header (Table.Toolbar + Table.Title for a title and actions), a Table.Content scroll region that holds the <table>, and the semantic parts Table.Head / Table.Body / Table.Foot / Table.Row / Table.HeaderCell / Table.Cell. The rounded border clips its content, so the corners stay intact even when the scroll region shows a scrollbar.

It carries no variant×color axis — a table is structural. The only knobs are size (density), striped, and stickyHeader / stickyFooter. For a data-driven, sortable/filterable table, reach for DataTable, which renders through this primitive.

import { Table } from '@42/ui-react/table';

<Table>
  <Table.Toolbar>
    <Table.Title>Members</Table.Title>
    <Button size="sm">Add member</Button>
  </Table.Toolbar>
  <Table.Content>
    <Table.Head>
      <Table.Row>
        <Table.HeaderCell>Name</Table.HeaderCell>
        <Table.HeaderCell>Role</Table.HeaderCell>
        <Table.HeaderCell className="text-right">Commits</Table.HeaderCell>
      </Table.Row>
    </Table.Head>
    <Table.Body>
      <Table.Row>
        <Table.Cell>Ada Lovelace</Table.Cell>
        <Table.Cell>Staff</Table.Cell>
        <Table.Cell className="text-right">128</Table.Cell>
      </Table.Row>
    </Table.Body>
  </Table.Content>
</Table>

Basic

The table parts go inside Table.Content. Numeric columns read best right-aligned — pass className="text-right" to the header and cells (there's no align prop on the primitive; DataTable adds per-column alignment).

NameRoleCommits
Ada LovelaceStaff128
Alan TuringStudent57
Grace HopperStaff203

Code

<Table>  <Table.Content>    <Table.Head>      <Table.Row>        <Table.HeaderCell>Name</Table.HeaderCell>        <Table.HeaderCell>Role</Table.HeaderCell>        <Table.HeaderCell className="text-right">Commits</Table.HeaderCell>      </Table.Row>    </Table.Head>    <Table.Body>      <Table.Row>        <Table.Cell>Ada Lovelace</Table.Cell>        <Table.Cell>Staff</Table.Cell>        <Table.Cell className="text-right">128</Table.Cell>      </Table.Row>    </Table.Body>  </Table.Content></Table>

Header with title & actions

Table.Toolbar is a non-<table> header bar above the scroll region — a title on the left (Table.Title) and any actions on the right. It's separated from the table by a border and stays put when the body scrolls.

Members

NameRole
Ada LovelaceStaff
Alan TuringStudent

Code

<Table>  <Table.Toolbar>    <Table.Title>Members</Table.Title>    <Button size="sm">Add member</Button>  </Table.Toolbar>  <Table.Content>{/* … */}</Table.Content></Table>

Striped

striped tints even body rows — easier to scan across wide rows. It targets tbody rows only, so the header is untouched.

NameRole
Ada LovelaceStaff
Alan TuringStudent
Grace HopperStaff
Katherine JohnsonStaff

Code

<Table striped>  <Table.Content>{/* … */}</Table.Content></Table>

Density

size scales cell padding and text from xs to xl (default md), reusing the kit's canonical Size scale.

Compact (sm)Role
Ada LovelaceStaff
Alan TuringStudent
Roomy (lg)Role
Ada LovelaceStaff
Alan TuringStudent

Code

<Table size="sm">{/* … */}</Table><Table size="lg">{/* … */}</Table>

stickyHeader pins the header to the top while the body scrolls; stickyFooter pins a Table.Foot row (a totals/summary row) to the bottom. Both need a bounded height on the scroll region — set max-h-* on Table (bounds the whole surface, toolbar included) or on Table.Content (bounds just the scroll region). The separators are cell borders (border-collapse: separate), so they stay visible while the header/footer are pinned, and the rounded border keeps its corners around the scrollbar.

#NameCommits
1Contributor 17
2Contributor 214
3Contributor 321
4Contributor 428
5Contributor 535
6Contributor 642
7Contributor 749
8Contributor 856
9Contributor 963
10Contributor 1070
11Contributor 1177
12Contributor 1284
13Contributor 1391
14Contributor 1498
15Contributor 15105
16Contributor 16112
17Contributor 17119
18Contributor 18126
19Contributor 19133
20Contributor 20140
Total1470

Code

<Table stickyHeader stickyFooter striped className="max-h-64">  <Table.Content>    <Table.Head>{/* … */}</Table.Head>    <Table.Body>{/* many rows */}</Table.Body>    <Table.Foot>{/* pinned totals row */}</Table.Foot>  </Table.Content></Table>

Horizontal + vertical scroll

Table.Content scrolls on both axes at once. A wide table (more columns than fit) scrolls horizontally, while a bounded height (max-h-*) plus stickyHeader scrolls it vertically with the header pinned — the header scrolls sideways in lockstep with the body so columns stay aligned. Both scrollbars are clipped by the rounded container, so its corners stay clean. Keep cell content on one line with whitespace-nowrap so columns don't wrap instead of overflowing.

A soft shadow appears on each horizontal side that still has content to scroll to, and fades out as you reach that edge — a scroll affordance driven purely by scroll position in CSS (no JS, so Table stays a Server Component). Browsers without scroll-driven animations simply show no fade.

#NameEmailRoleTeamLocationCommitsPRsReviewsLast active
1Contributor 1user1@42.techStaffOrderParis, FR7232024-01-15
2Contributor 2user2@42.techStudentAlianceParis, FR14462024-02-15
3Contributor 3user3@42.techStudentFederationParis, FR21692024-03-15
4Contributor 4user4@42.techStaffAssemblyParis, FR288122024-04-15
5Contributor 5user5@42.techStudentOrderParis, FR3510152024-05-15
6Contributor 6user6@42.techStudentAlianceParis, FR4212182024-06-15
7Contributor 7user7@42.techStaffFederationParis, FR4914212024-07-15
8Contributor 8user8@42.techStudentAssemblyParis, FR5616242024-08-15
9Contributor 9user9@42.techStudentOrderParis, FR6318272024-09-15
10Contributor 10user10@42.techStaffAlianceParis, FR7020302024-10-15
11Contributor 11user11@42.techStudentFederationParis, FR7722332024-11-15
12Contributor 12user12@42.techStudentAssemblyParis, FR8424362024-12-15
13Contributor 13user13@42.techStaffOrderParis, FR9126392024-01-15
14Contributor 14user14@42.techStudentAlianceParis, FR9828422024-02-15
15Contributor 15user15@42.techStudentFederationParis, FR10530452024-03-15
16Contributor 16user16@42.techStaffAssemblyParis, FR11232482024-04-15
17Contributor 17user17@42.techStudentOrderParis, FR11934512024-05-15
18Contributor 18user18@42.techStudentAlianceParis, FR12636542024-06-15

Code

<Table stickyHeader className="max-h-72">  <Table.Content classNames={{ table: 'min-w-[48rem]' }}>    <Table.Head>      <Table.Row>        <Table.HeaderCell>#</Table.HeaderCell>        <Table.HeaderCell>Name</Table.HeaderCell>        <Table.HeaderCell>Email</Table.HeaderCell>        {/* …more columns… */}      </Table.Row>    </Table.Head>    <Table.Body>{/* many wide rows */}</Table.Body>  </Table.Content></Table>

Table.Foot renders a <tfoot> with a top separator — a natural home for totals.

NameCommits
Ada Lovelace128
Alan Turing57
Grace Hopper203
Total388

Code

<Table>  <Table.Content>    <Table.Head>{/* … */}</Table.Head>    <Table.Body>{/* … */}</Table.Body>    <Table.Foot>      <Table.Row>        <Table.Cell>Total</Table.Cell>        <Table.Cell className="text-right">388</Table.Cell>      </Table.Row>    </Table.Foot>  </Table.Content></Table>

Subcomponents

Prop

Type

API

Prop

Type

On this page