Skip to content

Sortable Data Table

A dark data table with sticky header, three-state sort and rows that glide to their new places, highlighted search, shift-click selection with a floating bulk bar, sparklines, skeletons and phone cards.

DOM + CSS0 deps1 file · 43 KB TSXreduced-motion aware

Prominimaleditorial
Preview

Props

5
Show search
Row selection

The source is part of Pro

$27/month or $179 once. See Pro

No runtime dependencies
"use client";

import {
  useEffect,
  useLayoutEffect,
  useMemo,
  useRef,
  useState,
  useSyncExternalStore,
} from "react";
import type { CSSProperties, KeyboardEvent, MouseEvent, PointerEvent, ReactNode } from "react";

/* ---------------------------------------------------------------------------
 * Types
 * ------------------------------------------------------------------------- */

export type DataTableRow = {
  id: string;

  // ---------------------------------------------------------------
  // Full source available with a viberdy Pro subscription.
  // https://viberdy.dev/pricing
  // ---------------------------------------------------------------
}
Details

About this component

A client-side data table with the parts that are fiddly to get right. Column headers cycle ascending, descending, then back to the original order, with an arrow that flips on a spring and a glowing underline that sweeps in from the side the sort runs. Rows are keyed by id and re-ordered with a FLIP animation: they measure, re-render, then glide from where they were to where they are in about 260ms on a damped-spring curve; rows that appear (search, show more, first load) rise in with a short stagger. Search matches the text and status columns and marks the matched characters, with a live result count. Selection works from checkboxes, shift-click ranges and the keyboard, and a floating bar springs up with Copy CSV, Pause and Remove (with Undo). Status chips carry a soft glowing dot, revenue is right-aligned in tabular numerals with an inline sparkline per row, the first load shows a shimmering skeleton, and an empty search shows a designed empty state. pageSize pages the list with Show more / Show fewer. Below about 512px of container width, rows become stacked cards and the header becomes a row of sort pills. It takes optional rows and columns props (columns choose a kind: text, muted, status, currency, number or spark), so you can pass your own data. React only, no dependencies; it uses Tailwind v4 container-query variants (@lg:). It sorts and filters the whole array in memory, so reach for server-side paging or virtualisation beyond a few hundred rows.

Curator’s note

Rebuilt in October from a plain sortable list into a full table: the sort cycle still ends back at the original order, and the rows now move instead of jumping.

Pairs well with

Density Ledger

components
Pro
Pro