Recipes · Product

Data tables

A workspace members table with sticky header, striped rows, inline status badges, and empty-friendly design.

Preview

The exact composition below is what ships in production apps built on Nova.

NameTeamRoleStatus
Ada LovelaceEngineeringOwnerActive
Grace HopperDesignAdminActive
Radia PerlmanEngineeringMemberInvited
Katherine JohnsonProductAdminActive

Source

tsx
export function MembersTable({ rows }) {
return (
<table className="w-full text-sm">
<thead className="bg-subtle/60 text-left">
<tr>
<th>Name</th><th>Team</th><th>Role</th><th>Status</th>
</tr>
</thead>
<tbody>
{rows.map((r) => (
<tr key={r.id} className="border-t">
<td className="px-3 py-2 font-medium">{r.name}</td>
<td className="px-3 py-2 text-muted-foreground">{r.team}</td>
<td className="px-3 py-2 text-muted-foreground">{r.role}</td>
<td className="px-3 py-2">
<Badge variant="outline" size="sm">{r.status}</Badge>
</td>
</tr>
))}
</tbody>
</table>
);
}

Anatomy

  • Header uses uppercase mono at 11px for a data-heavy feel.
  • Row separators via 1px borders — never full-width strong dividers.
  • Status column always uses Badge for consistent semantics.
  • Wrap the table in overflow-x-auto for narrow viewports.