Skip to content

Your first grid in 5 minutes

You have installed the grid. Here is the shortest path to one that looks like it belongs in your app, with the toolbar, pager, selection and editing on.

  1. Describe a row. The grid is typed from this.

    customers/columns.tsx
    export type Customer = {
    id: string
    name: string
    email: string
    plan: "free" | "pro" | "team"
    mrr: number
    active: boolean
    joinedAt: Date
    }
  2. Build the columns. createDataGridColumns<Customer>() gives you one builder per column kind. Keys are checked against Customer; each kind brings the right sort, filter, editor, aggregation and format.

    customers/columns.tsx
    import { createDataGridColumns } from "@/components/ui/data-grid"
    const col = createDataGridColumns<Customer>()
    export const columns = col.columns([
    col.select(),
    col.text("name", { header: "Name", editable: true }),
    col.link("email", { header: "Email", href: (v) => `mailto:${v}` }),
    col.enum("plan", {
    header: "Plan",
    options: [
    { value: "free", label: "Free" },
    { value: "pro", label: "Pro" },
    { value: "team", label: "Team" },
    ],
    editable: true,
    }),
    col.currency("mrr", { header: "MRR", currency: "USD" }),
    col.boolean("active", { header: "Active" }),
    col.date("joinedAt", { header: "Joined" }),
    ])
  3. Render it. data and columns are all it needs; getRowId keeps selection and editing stable when rows move.

    customers/page.tsx
    import { DataGrid } from "@/components/ui/data-grid"
    import { columns, type Customer } from "./columns"
    export function CustomersPage({ customers }: { customers: Customer[] }) {
    return <DataGrid data={customers} columns={columns} getRowId={(c) => c.id} />
    }
  4. Save edits. onCellEdit fires after a cell is committed, with the row, the column id and the typed value. Throw (or return a rejected promise) to keep the editor open with the message.

    customers/page.tsx
    <DataGrid
    data={customers}
    columns={columns}
    onCellEdit={async ({ row, columnId, value }) => {
    await api.customers.update(row.id, { [columnId]: value })
    // then refetch, or update local state
    }}
    />
  5. Act on a selection. Whatever you render in bulkActions appears in a bar while rows are selected, with the selected rows as the argument.

    customers/page.tsx
    <DataGrid
    data={customers}
    columns={columns}
    bulkActions={(rows) => (
    <Button variant="destructive" size="sm" onClick={() => archive(rows.map((r) => r.id))}>
    Archive {rows.length}
    </Button>
    )}
    />

That is the whole thing. Here it is with every feature on, on demo data:

Yuki Khanyuki.khan@mail.testChurnedTeamGrowthDirector$0.002174.6%Sep 2, 2023May 5, 2025, 10:52 PMnps-9Japan—
Mateo Rossimateo.rossi@corp.exampleInvitedFreePlatformPM$0.0016.2%Oct 21, 2024Jul 7, 2026, 9:33 PM—GermanyRenewal in Q1.
Aarav Sharmaaarav.sharma@example.comChurnedProSupportLead$0.00224.5%Feb 6, 2026May 4, 2026, 8:56 PM—France—
Vihaan Tanakavihaan.tanaka@corp.exampleChurnedProFinanceManager$0.00292.8%Oct 14, 2023Aug 27, 2024, 3:51 AM—IndiaRenewal in Q1.
Mia Menonmia.menon@example.comActiveFreeFinanceDirector$0.00127.8%May 19, 2024Jul 27, 2025, 2:01 PMnps-9Canada—
Mateo Nguyenmateo.nguyen@corp.exampleActiveEnterpriseDesignManager$2,449.003129.5%Feb 27, 2025Sep 15, 2026, 11:14 AMapacBrazilSpeaks at meetups.
Theo Silvatheo.silva@team.devActiveProResearchEngineer$36.00347.5%Feb 19, 2024May 29, 2024, 11:16 AM—Australia—
Theo Sharmatheo.sharma@example.comPausedTeamFinanceDirector$406.001432.6%Nov 9, 2023Feb 2, 2025, 6:06 PMapacCanada—
Diya Silvadiya.silva@corp.exampleActiveFreeDataDesigner$0.0014.7%Oct 29, 2025Jun 23, 2026, 7:48 AMchurn-riskeuCanadaAsked about invoices.
Saanvi Patelsaanvi.patel@corp.exampleActiveFreeDesignAnalyst$0.00143.4%Oct 23, 2026Oct 31, 2026, 5:04 AMvipBrazil—
0 of 200 row(s) selected.
Page 1 of 20
  • Columns: every kind, every option, custom cells and headers.
  • Server-side data when the rows live behind an API.
  • Composition when the default layout isn’t yours.
  • Theming to make it look like your product (it already mostly does).