Kosmesis
Components

Table

A responsive table with Header/Body/Footer/Row/Head/Cell/Caption parts. Purely presentational.

Installation

npx kosmesis add table
pnpm dlx kosmesis add table
yarn dlx kosmesis add table
bunx kosmesis add table

Copy and paste the following code into your project.

table.tsx
import { StatelessComponent } from "@praxisjs/core";import { Component } from "@praxisjs/decorators";import type { Children } from "@praxisjs/shared";import { cn } from "@/lib/utils";export interface TableSlotProps {  class?: string;  id?: string;  children?: Children;}/** Purely presentational — no Morphos equivalent, same as upstream shadcn/ui. */@Component()export class Table extends StatelessComponent<TableSlotProps> {  render() {    const { class: cls, id, children } = this.props;    return (      <div data-slot="table-container" class="relative w-full overflow-x-auto">        <table id={id} data-slot="table" class={cn("w-full caption-bottom text-sm", cls)}>          {children}        </table>      </div>    );  }}@Component()export class TableHeader extends StatelessComponent<TableSlotProps> {  render() {    const { class: cls, id, children } = this.props;    return (      <thead id={id} data-slot="table-header" class={cn("[&_tr]:border-b", cls)}>        {children}      </thead>    );  }}@Component()export class TableBody extends StatelessComponent<TableSlotProps> {  render() {    const { class: cls, id, children } = this.props;    return (      <tbody id={id} data-slot="table-body" class={cn("[&_tr:last-child]:border-0", cls)}>        {children}      </tbody>    );  }}@Component()export class TableFooter extends StatelessComponent<TableSlotProps> {  render() {    const { class: cls, id, children } = this.props;    return (      <tfoot        id={id}        data-slot="table-footer"        class={cn("border-t bg-muted/50 font-medium [&>tr]:last:border-b-0", cls)}      >        {children}      </tfoot>    );  }}@Component()export class TableRow extends StatelessComponent<TableSlotProps> {  render() {    const { class: cls, id, children } = this.props;    return (      <tr        id={id}        data-slot="table-row"        class={cn("border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted", cls)}      >        {children}      </tr>    );  }}export interface TableHeadProps extends TableSlotProps {  onClick?: (event: MouseEvent) => void;}@Component()export class TableHead extends StatelessComponent<TableHeadProps> {  render() {    const { class: cls, id, children, onClick } = this.props;    return (      <th        id={id}        data-slot="table-head"        class={cn(          "h-10 whitespace-nowrap px-2 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0",          cls,        )}        onClick={onClick}      >        {children}      </th>    );  }}@Component()export class TableCell extends StatelessComponent<TableSlotProps> {  render() {    const { class: cls, id, children } = this.props;    return (      <td        id={id}        data-slot="table-cell"        class={cn("whitespace-nowrap p-2 align-middle [&:has([role=checkbox])]:pr-0", cls)}      >        {children}      </td>    );  }}@Component()export class TableCaption extends StatelessComponent<TableSlotProps> {  render() {    const { class: cls, id, children } = this.props;    return (      <caption id={id} data-slot="table-caption" class={cn("mt-4 text-sm text-muted-foreground", cls)}>        {children}      </caption>    );  }}

Install the following dependencies:

npm install @praxisjs/css
pnpm add @praxisjs/css
yarn add @praxisjs/css
bun add @praxisjs/css

Copy and paste the following code into your project.

table.tsx
import { StatelessComponent } from "@praxisjs/core";import { cx, Stylesheet, Styled, tokenVars } from "@praxisjs/css";import { Component } from "@praxisjs/decorators";import type { Children } from "@praxisjs/shared";import { KosmesisTokens } from "@/lib/kosmesis-theme";const t = tokenVars(KosmesisTokens);class TableStyles extends Stylesheet {  $container = this.css({ position: "relative", width: "100%", overflowX: "auto" });  $table = this.css({ width: "100%", captionSide: "bottom", fontSize: "0.875rem" });  $header = this.css({}).on("& tr", { borderBottom: `1px solid ${t.border}` });  $body = this.css({}).on("& tr:last-child", { borderBottom: "none" });  $footer = this.css({ borderTop: `1px solid ${t.border}`, backgroundColor: `color-mix(in oklab, ${t.muted} 50%, transparent)`, fontWeight: 500 }).on(    "& > tr:last-child",    { borderBottom: "none" },  );  $row = this.css({ borderBottom: `1px solid ${t.border}`, transition: "background-color 120ms ease" })    .hover({ backgroundColor: `color-mix(in oklab, ${t.muted} 50%, transparent)` })    .on('&[data-state="selected"]', { backgroundColor: t.muted });  $head = this.css({    height: "2.5rem",    whiteSpace: "nowrap",    padding: "0 0.5rem",    textAlign: "left",    verticalAlign: "middle",    fontWeight: 500,    color: t.mutedForeground,  }).has('[role="checkbox"]', { paddingRight: "0" });  $cell = this.css({ whiteSpace: "nowrap", padding: "0.5rem", verticalAlign: "middle" }).has('[role="checkbox"]', {    paddingRight: "0",  });  $caption = this.css({ marginTop: "1rem", fontSize: "0.875rem", color: t.mutedForeground });}export interface TableSlotProps {  class?: string;  id?: string;  children?: Children;}/** Purely presentational — no Morphos equivalent, same as upstream shadcn/ui. */@Component()export class Table extends StatelessComponent<TableSlotProps> {  @Styled(TableStyles) $s!: TableStyles;  render() {    const { class: cls, id, children } = this.props;    return (      <div data-slot="table-container" class={this.$s.$container}>        <table id={id} data-slot="table" class={cx(this.$s.$table, cls)}>          {children}        </table>      </div>    );  }}@Component()export class TableHeader extends StatelessComponent<TableSlotProps> {  @Styled(TableStyles) $s!: TableStyles;  render() {    const { class: cls, id, children } = this.props;    return (      <thead id={id} data-slot="table-header" class={cx(this.$s.$header, cls)}>        {children}      </thead>    );  }}@Component()export class TableBody extends StatelessComponent<TableSlotProps> {  @Styled(TableStyles) $s!: TableStyles;  render() {    const { class: cls, id, children } = this.props;    return (      <tbody id={id} data-slot="table-body" class={cx(this.$s.$body, cls)}>        {children}      </tbody>    );  }}@Component()export class TableFooter extends StatelessComponent<TableSlotProps> {  @Styled(TableStyles) $s!: TableStyles;  render() {    const { class: cls, id, children } = this.props;    return (      <tfoot id={id} data-slot="table-footer" class={cx(this.$s.$footer, cls)}>        {children}      </tfoot>    );  }}@Component()export class TableRow extends StatelessComponent<TableSlotProps> {  @Styled(TableStyles) $s!: TableStyles;  render() {    const { class: cls, id, children } = this.props;    return (      <tr id={id} data-slot="table-row" class={cx(this.$s.$row, cls)}>        {children}      </tr>    );  }}export interface TableHeadProps extends TableSlotProps {  onClick?: (event: MouseEvent) => void;}@Component()export class TableHead extends StatelessComponent<TableHeadProps> {  @Styled(TableStyles) $s!: TableStyles;  render() {    const { class: cls, id, children, onClick } = this.props;    return (      <th id={id} data-slot="table-head" class={cx(this.$s.$head, cls)} onClick={onClick}>        {children}      </th>    );  }}@Component()export class TableCell extends StatelessComponent<TableSlotProps> {  @Styled(TableStyles) $s!: TableStyles;  render() {    const { class: cls, id, children } = this.props;    return (      <td id={id} data-slot="table-cell" class={cx(this.$s.$cell, cls)}>        {children}      </td>    );  }}@Component()export class TableCaption extends StatelessComponent<TableSlotProps> {  @Styled(TableStyles) $s!: TableStyles;  render() {    const { class: cls, id, children } = this.props;    return (      <caption id={id} data-slot="table-caption" class={cx(this.$s.$caption, cls)}>        {children}      </caption>    );  }}

Examples

Usage

import {
  Table,
  TableBody,
  TableCaption,
  TableCell,
  TableHead,
  TableHeader,
  TableRow,
} from "@/components/ui/table";

<Table>
  <TableCaption>A list of recent invoices.</TableCaption>
  <TableHeader>
    <TableRow>
      <TableHead>Invoice</TableHead>
      <TableHead>Status</TableHead>
      <TableHead class="text-right">Amount</TableHead>
    </TableRow>
  </TableHeader>
  <TableBody>
    <TableRow>
      <TableCell>INV001</TableCell>
      <TableCell>Paid</TableCell>
      <TableCell class="text-right">$250.00</TableCell>
    </TableRow>
  </TableBody>
</Table>

For sortable/paginated tables, see Data Table.

Props

TableHead

PropTypeDefault
onClick(event: MouseEvent) => void

Table, TableHeader, TableBody, TableFooter, TableRow, TableCell, and TableCaption are purely layout — they take no props beyond class/id/children.

On this page