Table

Composable data table with head, body, row, header, and cell sub-components — supports striped rows and hoverable styling.

Preview

Basic table

import { Table, TableHead, TableBody, TableRow, TableHeader, TableCell } from '@/registry/ui-kit/tailwind-table/react'

function Example() {
  return (
    <Table>
      <TableHead>
        <TableRow>
          <TableHeader>Name</TableHeader>
          <TableHeader>Email</TableHeader>
          <TableHeader>Role</TableHeader>
        </TableRow>
      </TableHead>
      <TableBody>
        <TableRow>
          <TableCell>Alice Johnson</TableCell>
          <TableCell>alice@example.com</TableCell>
          <TableCell>Admin</TableCell>
        </TableRow>
      </TableBody>
    </Table>
  )
}

Striped table

import { Table, TableHead, TableBody, TableRow, TableHeader, TableCell } from '@/registry/ui-kit/tailwind-table/react'

function Example() {
  return (
    <Table striped>
      <TableHead>
        <TableRow>
          <TableHeader>Name</TableHeader>
          <TableHeader>Email</TableHeader>
        </TableRow>
      </TableHead>
      <TableBody>
        <TableRow>
          <TableCell>Alice</TableCell>
          <TableCell>alice@example.com</TableCell>
        </TableRow>
        <TableRow>
          <TableCell>Bob</TableCell>
          <TableCell>bob@example.com</TableCell>
        </TableRow>
      </TableBody>
    </Table>
  )
}

Component API

PropDefaultDescription
TableRoot wrapper with overflow-x-auto scroll container.
stripedfalseAdds alternating row background colors.
hoverablefalseAdds hover highlight on rows.
TableHeadThe <thead> element.
TableBodyThe <tbody> with divide-y styling.
TableRowA <tr> row element.
TableHeaderA <th> header cell with uppercase tracking.
TableCellA <td> data cell.

Source Code

"use client";

import { createContext, forwardRef, useContext } from "react";

/* ── Context ── */

interface TableContextValue {
  striped: boolean;
  hoverable: boolean;
}

const TableContext = createContext<TableContextValue>({ striped: false, hoverable: false });

function useTableContext() {
  return useContext(TableContext);
}

/* ── Table ── */

interface TableProps extends React.HTMLAttributes<HTMLTableElement> {
  striped?: boolean;
  hoverable?: boolean;
}

const Table = forwardRef<HTMLTableElement, TableProps>(
  ({ striped = false, hoverable = false, className = "", children, ...props }, ref) => (
    <TableContext.Provider value={{ striped, hoverable }}>
      <div className="overflow-x-auto">
        <table
          ref={ref}
          className={`min-w-full divide-y divide-zinc-200 dark:divide-zinc-800 ${className}`}
          {...props}
        >
          {children}
        </table>
      </div>
    </TableContext.Provider>
  )
);
Table.displayName = "Table";

/* ── TableHead ── */

const TableHead = forwardRef<HTMLTableSectionElement, React.HTMLAttributes<HTMLTableSectionElement>>(
  ({ className = "", children, ...props }, ref) => (
    <thead ref={ref} className={className} {...props}>
      {children}
    </thead>
  )
);
TableHead.displayName = "TableHead";

/* ── TableBody ── */

const TableBody = forwardRef<HTMLTableSectionElement, React.HTMLAttributes<HTMLTableSectionElement>>(
  ({ className = "", children, ...props }, ref) => {
    const { striped } = useTableContext();
    const stripedStyles = striped
      ? "[&>tr:nth-child(even)]:bg-zinc-50 dark:[&>tr:nth-child(even)]:bg-zinc-800/50"
      : "";

    return (
      <tbody
        ref={ref}
        className={`divide-y divide-zinc-100 dark:divide-zinc-800 ${stripedStyles} ${className}`}
        {...props}
      >
        {children}
      </tbody>
    );
  }
);
TableBody.displayName = "TableBody";

/* ── TableRow ── */

const TableRow = forwardRef<HTMLTableRowElement, React.HTMLAttributes<HTMLTableRowElement>>(
  ({ className = "", children, ...props }, ref) => {
    const { hoverable } = useTableContext();
    const hoverStyles = hoverable ? "hover:bg-zinc-50 dark:hover:bg-zinc-800/50" : "";

    return (
      <tr ref={ref} className={`${hoverStyles} ${className}`} {...props}>
        {children}
      </tr>
    );
  }
);
TableRow.displayName = "TableRow";

/* ── TableHeader ── */

const TableHeader = forwardRef<HTMLTableCellElement, React.ThHTMLAttributes<HTMLTableCellElement>>(
  ({ className = "", children, ...props }, ref) => (
    <th
      ref={ref}
      className={`px-4 py-3 text-left text-xs font-medium uppercase tracking-wide text-zinc-500 dark:text-zinc-400 ${className}`}
      {...props}
    >
      {children}
    </th>
  )
);
TableHeader.displayName = "TableHeader";

/* ── TableCell ── */

const TableCell = forwardRef<HTMLTableCellElement, React.TdHTMLAttributes<HTMLTableCellElement>>(
  ({ className = "", children, ...props }, ref) => (
    <td
      ref={ref}
      className={`whitespace-nowrap px-4 py-3 text-sm text-zinc-900 dark:text-zinc-100 ${className}`}
      {...props}
    >
      {children}
    </td>
  )
);
TableCell.displayName = "TableCell";

export { Table, TableHead, TableBody, TableRow, TableHeader, TableCell };
export default Table;