Skip to content

Installation

The grid is distributed as a shadcn registry. You need a React 19 project with Tailwind CSS v4 and shadcn/ui set up for Base UI (the default since July 2026). Next.js, Vite, Astro, Remix, React Router and TanStack Start all work.

  • React 19 and TypeScript 5.5 or later (7 recommended).
  • Tailwind CSS v4.
  • shadcn/ui initialised with Base UI: npx shadcn@latest init -b base. On an existing Radix project, the grid’s menu and popover code expects Base UI’s render prop API; see the FAQ for your options.
  1. Register the @kadal namespace once, in components.json:

    components.json
    {
    "$schema": "https://ui.shadcn.com/schema.json",
    "registries": {
    "@kadal": "https://ui.kadal.cc/r/{name}.json"
    }
    }
  2. Add the grid:

    Terminal window
    npx shadcn@latest add @kadal/data-grid
  3. Optional: the URL-state hook, the demo data and the examples.

    Terminal window
    npx shadcn@latest add @kadal/data-grid-url-state @kadal/data-grid-demo
  • Copies 21 TypeScript files into components/ui/data-grid/ (the folder your components.json ui alias points at). Import from @/components/ui/data-grid.
  • Adds the shadcn primitives it uses if you don’t have them: badge, button, checkbox, dialog, dropdown-menu, empty, input, label, popover, select, spinner, textarea and tooltip. Files you already have are left alone.
  • Installs @tanstack/react-table@^9.3.1, @tanstack/react-virtual@^3.14.26, @dnd-kit/react@^0.5.3, @dnd-kit/dom@^0.5.3 and lucide-react with your package manager.

No CSS file to import: the grid is Tailwind classes over your theme tokens (--background, --primary, --muted and friends), so it takes your theme and dark mode as they are.

Your copy doesn’t update itself, which is the point. When a new version is out:

Terminal window
npx shadcn@latest add @kadal/data-grid --diff # what changed, file by file
npx shadcn@latest add @kadal/data-grid --overwrite # take the new version

Keep your own changes in files next to the grid (a custom cell, a column kind, a toolbar) rather than inside it, and the overwrite stays painless. The changelog lists every change to the registry by version.

app/customers/page.tsx
import { DataGrid, createDataGridColumns } from "@/components/ui/data-grid"
type Customer = { id: string; name: string; plan: "free" | "pro"; mrr: number }
const col = createDataGridColumns<Customer>()
const columns = col.columns([
col.text("name", { header: "Name" }),
col.enum("plan", { options: [{ value: "free", label: "Free" }, { value: "pro", label: "Pro" }] }),
col.currency("mrr", { header: "MRR" }),
])
export default function Page({ customers }: { customers: Customer[] }) {
return <DataGrid data={customers} columns={columns} getRowId={(c) => c.id} />
}

If that renders a grid with a search box, sortable headers and a pager, you’re set. Next: your first grid in 5 minutes.