Hooks

useTableSort

Use for data tables that need external sort state without baking sorting behavior into the Table primitive.

Import

import { useTableSort } from "@zentauri-ui/zentauri-components/hooks/useTableSort";

Demo source

Full client component used for this preview (same JSX as below). Adjust paths if you copy outside this app.

"use client";

import { useTableSort } from "@zentauri-ui/zentauri-components/hooks/useTableSort";
import { Button } from "@zentauri-ui/zentauri-components/ui/buttons";
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@zentauri-ui/zentauri-components/ui/table";
import { HookDemoPanel } from "@/components/preview/hooks/demo-panel";
import { useMemo } from "react";

type Row = { name: string; team: string; tickets: number };
type SortKey = keyof Row;

const rows: Row[] = [
  { name: "Avery Stone", team: "Support", tickets: 24 },
  { name: "Mira Chen", team: "Platform", tickets: 16 },
  { name: "Noah Rivera", team: "Design", tickets: 31 },
  { name: "Priya Shah", team: "Support", tickets: 12 },
];

function compareValues(a: string | number, b: string | number) {
  return typeof a === "number" && typeof b === "number"
    ? a - b
    : String(a).localeCompare(String(b));
}

export default function HookDemo() {
  const { sortKey, sortDirection, getSortProps, clearSort } =
    useTableSort<SortKey>({
      defaultSortKey: "name",
      defaultSortDirection: "ascending",
    });

  const sortedRows = useMemo(() => {
    if (!sortKey || sortDirection === "none") return rows;
    const activeSortKey = sortKey as SortKey;
    return [...rows].sort((a, b) => {
      const result = compareValues(a[activeSortKey], b[activeSortKey]);
      return sortDirection === "ascending" ? result : -result;
    });
  }, [sortDirection, sortKey]);

  return (
    <HookDemoPanel title="Interactive demo">
      <div className="mb-4 flex flex-wrap items-center justify-between gap-3">
        <p className="text-sm text-slate-400">
          Click a sortable header to cycle ascending, descending, and none.
        </p>
        <Button type="button" size="sm" appearance="outline" onClick={clearSort}>
          Clear sort
        </Button>
      </div>
      <Table appearance="bordered" size="sm">
        <TableHeader>
          <TableRow>
            <TableHead {...getSortProps("name")}>Name</TableHead>
            <TableHead {...getSortProps("team")}>Team</TableHead>
            <TableHead {...getSortProps("tickets")} className="text-right">
              Tickets
            </TableHead>
          </TableRow>
        </TableHeader>
        <TableBody>
          {sortedRows.map((row) => (
            <TableRow key={row.name}>
              <TableCell>{row.name}</TableCell>
              <TableCell>{row.team}</TableCell>
              <TableCell className="text-right tabular-nums">{row.tickets}</TableCell>
            </TableRow>
          ))}
        </TableBody>
      </Table>
      <pre className="mt-4 overflow-auto rounded-lg border border-white/10 bg-slate-950/80 p-3 text-xs text-slate-300">
        {JSON.stringify({ sortKey, sortDirection }, null, 2)}
      </pre>
    </HookDemoPanel>
  );
}

Interactive demo

Click a sortable header to cycle ascending, descending, and none.

NameTeamTickets
Avery StoneSupport24
Mira ChenPlatform16
Noah RiveraDesign31
Priya ShahSupport12
{
  "sortKey": "name",
  "sortDirection": "ascending"
}

What it does

useTableSort ships as a small client module you can import from @zentauri-ui/zentauri-components/hooks/useTableSort. Use it when the behavior matches your integration without copying utility code.

API notes

Read the interactive demo on this page for the parameters used in typical flows. Refer to TypeScript types exported next to the hook for exhaustive options.

Common use cases

  • Reuse the same hook in client components across Next.js App Router routes.
  • Keep server components free of browser APIs; colocate interactive demos in client files.
  • Compose hooks with Zentauri UI primitives for overlays, forms, and data views.
  • Align documentation with published @zentauri-ui/zentauri-components/hooks/* paths.

Accessibility

Hooks manipulate behavior, not roles. Pair them with appropriate elements, ARIA attributes, and keyboard handlers so assistive technologies receive a coherent experience.

Next.js integration notes

Keep interactive subtrees in client components. When you only need static copy, leave it in the server layout and pass children into a client boundary that calls the hook.

FAQ

Can I import this hook from a server component?

No. These hooks rely on browser APIs or React client lifecycle. Import them from a file marked with the "use client" directive or from a client child component.

Does the preview site bundle the same code as npm?

The component library app depends on the workspace package. Published builds resolve the same export paths under @zentauri-ui/zentauri-components.

Where should I validate accessibility for hook-driven UI?

Hooks do not replace semantics. Test focus order, labels, and keyboard flows in your real layout with assistive technologies you support.