Table

Display tabular data in a grid with selection and sorting.

Documentation
React Aria
Pattern
W3C ARIA
Source
GitHub
Issues
Report
GamesFile folder
Program FilesFile folder
bootmgrSystem file
DocumentsFile folder
DownloadsFile folder
Table example
import {
  Cell,
  Column,
  Row,
  Table,
  TableBody,
  TableHeader,
} from "@/shim-ui/table";

export default () => (
  <Table aria-label="Files">
    <TableHeader>
      <Column isRowHeader>Name</Column>
      <Column>Type</Column>
    </TableHeader>
    <TableBody>
      <Row>
        <Cell>Games</Cell>
        <Cell>File folder</Cell>
      </Row>
      <Row>
        <Cell>Program Files</Cell>
        <Cell>File folder</Cell>
      </Row>
      <Row>
        <Cell>bootmgr</Cell>
        <Cell>System file</Cell>
      </Row>
      <Row>
        <Cell>Documents</Cell>
        <Cell>File folder</Cell>
      </Row>
      <Row>
        <Cell>Downloads</Cell>
        <Cell>File folder</Cell>
      </Row>
    </TableBody>
  </Table>
);

Install

Use the CLI or copy the source code manually.

pnpm dlx @kkga/shim add table

Content

Provide columns via the columns prop on <TableHeader>and data with items on <TableBody> to render dynamic tables.

Unicorn
Dragon
Mermaid
Bigfoot
Chimera
Content example
"use client";
import { CheckIcon, XIcon } from "@phosphor-icons/react";
import {
  Cell,
  Column,
  Row,
  Table,
  TableBody,
  TableHeader,
} from "@/shim-ui/table";

let columns = [
  { id: "creature", name: "Creature", isRowHeader: true },
  { id: "canFly", name: "Can fly" },
  { id: "hasHorn", name: "Has horn" },
  { id: "magical", name: "Magical" },
  { id: "scary", name: "Scary" },
];

let rows = [
  {
    id: 1,
    creature: "Unicorn",
    canFly: false,
    hasHorn: true,
    magical: true,
    scary: false,
  },
  {
    id: 2,
    creature: "Dragon",
    canFly: true,
    hasHorn: false,
    magical: true,
    scary: true,
  },
  {
    id: 3,
    creature: "Mermaid",
    canFly: false,
    hasHorn: false,
    magical: true,
    scary: false,
  },
  {
    id: 4,
    creature: "Bigfoot",
    canFly: false,
    hasHorn: false,
    magical: false,
    scary: true,
  },
  {
    id: 5,
    creature: "Chimera",
    canFly: false,
    hasHorn: true,
    magical: false,
    scary: true,
  },
];

const Check = () => (
  <CheckIcon className="text-success-text" size={16} weight="bold" />
);
const X = () => <XIcon className="text-neutral-text-subtle" size={16} />;

export default () => (
  <Table aria-label="Mythical Creatures">
    <TableHeader columns={columns}>
      {(column) => (
        <Column isRowHeader={column.isRowHeader}>{column.name}</Column>
      )}
    </TableHeader>
    <TableBody items={rows}>
      {({ creature, canFly, hasHorn, magical, scary }) => (
        <Row>
          <Cell>{creature}</Cell>
          <Cell>{canFly ? <Check /> : <X />}</Cell>
          <Cell>{hasHorn ? <Check /> : <X />}</Cell>
          <Cell>{magical ? <Check /> : <X />}</Cell>
          <Cell>{scary ? <Check /> : <X />}</Cell>
        </Row>
      )}
    </TableBody>
  </Table>
);

Sorting

Enable column sorting by providing a sortDescriptor with a column key and direction value.

Direction accepts "ascending" or "descending" and the column matches the column key.

Alice JohnsonMathematics3.9
Bob BrownPhysics3.7
Charlie DavisChemistry3.6
Diana EvansBiology3.8
Ethan FosterEconomics3.4
Fiona GreenPsychology3.7
Jane SmithMechanical Engineering3.8
John DoeComputer Science3.5
Sorting example
"use client";
import { useMemo, useState } from "react";
import type { SortDescriptor } from "react-aria-components";
import {
  Cell,
  Column,
  Row,
  Table,
  TableBody,
  TableHeader,
} from "@/shim-ui/table";

interface RowData {
  id: number;
  name: string;
  major: string;
  gpa: number;
}

let rows: RowData[] = [
  { id: 1, name: "John Doe", major: "Computer Science", gpa: 3.5 },
  { id: 2, name: "Jane Smith", major: "Mechanical Engineering", gpa: 3.8 },
  { id: 3, name: "Alice Johnson", major: "Mathematics", gpa: 3.9 },
  { id: 4, name: "Bob Brown", major: "Physics", gpa: 3.7 },
  { id: 5, name: "Charlie Davis", major: "Chemistry", gpa: 3.6 },
  { id: 6, name: "Diana Evans", major: "Biology", gpa: 3.8 },
  { id: 7, name: "Ethan Foster", major: "Economics", gpa: 3.4 },
  { id: 8, name: "Fiona Green", major: "Psychology", gpa: 3.7 },
];

export default () => {
  let [sortDescriptor, setSortDescriptor] = useState<SortDescriptor>({
    column: "name",
    direction: "ascending",
  });

  let items = useMemo(() => {
    let sorted = [...rows].sort((a, b) => {
      let column = sortDescriptor.column as keyof RowData;
      if (typeof a[column] === "number" && typeof b[column] === "number") {
        return a[column] - b[column];
      }
      if (typeof a[column] === "string" && typeof b[column] === "string") {
        return a[column].localeCompare(b[column]);
      }
      return 0;
    });
    if (sortDescriptor.direction === "descending") {
      sorted.reverse();
    }
    return sorted;
  }, [sortDescriptor]);

  return (
    <Table
      aria-label="Students"
      onSortChange={setSortDescriptor}
      sortDescriptor={sortDescriptor}
    >
      <TableHeader>
        <Column allowsSorting id="name" isRowHeader>
          Name
        </Column>
        <Column allowsSorting id="major">
          Major
        </Column>
        <Column allowsSorting id="gpa">
          GPA
        </Column>
      </TableHeader>
      <TableBody items={items}>
        {({ name, major, gpa }) => (
          <Row>
            <Cell>{name}</Cell>
            <Cell>{major}</Cell>
            <Cell className="tabular-nums">{gpa}</Cell>
          </Row>
        )}
      </TableBody>
    </Table>
  );
};