Glin UI
Primitive / Organismbeta

Data Table

Feature-rich table wrapper with search, sorting, pagination, selection, and column controls.

Edit on GitHub
Rows
MK
Maya Kovacsmaya@northwind.io
OwnerActive12
JO
Jonas Okaforjonas@northwind.io
AdminActive8
PN
Priya Nairpriya@fabrikam.dev
EditorInvited4
DA
Diego Alvarezdiego@contoso.com
ViewerSuspended1
HS
Hana Satohana@tailspin.jp
EditorActive6

Showing 1-5 of 7

Page 1 of 2

Installation

Install from
pnpm dlx @glinui/cli@latest add data-table

Prefer the package?

Usage

Grid and sticky header

Rows
Glin UIActive18400
Glin DocsMonitoring7920
RegistryActive12340

Showing 1-3 of 4

Page 1 of 2

import { Badge, DataTable } from "@glinui/ui"
const columns = [
{ id: "project", header: "Project", accessor: "project" },
{
id: "status",
header: "Status",
accessor: "status",
cell: ({ value }) => <Badge variant={value === "Active" ? "soft" : "outline"}>{String(value)}</Badge>
},
{ id: "mrr", header: "MRR", accessor: "mrr", align: "right" }
]
const data = [
{ id: "p1", project: "Glin UI", status: "Active", mrr: 18400 },
{ id: "p2", project: "Glin Docs", status: "Monitoring", mrr: 7920 },
{ id: "p3", project: "Registry", status: "Active", mrr: 12340 },
{ id: "p4", project: "CLI", status: "Paused", mrr: 2310 }
]
export function Demo() {
return (
<DataTable
columns={columns}
data={data}
selectable
striped
stickyHeader
grid
variant="soft"
pageSize={3}
getRowId={(row) => row.id}
/>
)
}

Compact Board With Sticky Header

import { DataTable } from "@glinui/ui"

export function DataTableCompactDemo() {
  return (
    <DataTable
      columns={[
        { id: "account", header: "Account", accessor: "account", sortable: true },
        { id: "stage", header: "Stage", accessor: "stage", sortable: true },
        { id: "owner", header: "Owner", accessor: "owner", sortable: true },
        { id: "arr", header: "ARR", accessor: "arr", sortable: true, align: "right" },
      ]}
      data={[
        { id: "op-4012", account: "Northwind", stage: "Qualified", owner: "Maya", arr: "$42,000" },
        { id: "op-4015", account: "Stellar Forge", stage: "Proposal", owner: "Ilya", arr: "$18,500" },
        { id: "op-4018", account: "Blue Harbor", stage: "Won", owner: "Rae", arr: "$63,200" },
        { id: "op-4021", account: "Arcadia Labs", stage: "Discovery", owner: "Nico", arr: "$11,900" },
        { id: "op-4022", account: "Halcyon", stage: "Qualified", owner: "Leah", arr: "$27,600" },
      ]}
      getRowId={(row) => row.id}
      variant="matte"
      size="sm"
      stickyHeader
      containerClassName="max-h-72"
      pageSize={5}
      searchable
    />
  )
}

Selection + Column Controls

Enable `selectable` and hide low-priority columns with `hidden`/`canHide` to keep dense dashboards readable.

import { DataTable } from "@glinui/ui"

export function DataTableSelectionDemo() {
  return (
    <DataTable
      columns={[
        { id: "account", header: "Account", accessor: "account", sortable: true },
        { id: "stage", header: "Stage", accessor: "stage", sortable: true },
        { id: "owner", header: "Owner", accessor: "owner", sortable: true, hidden: true, canHide: true },
        { id: "arr", header: "ARR", accessor: "arr", sortable: true, align: "right" },
      ]}
      data={[
        { id: "op-4012", account: "Northwind", stage: "Qualified", owner: "Maya", arr: "$42,000" },
        { id: "op-4015", account: "Stellar Forge", stage: "Proposal", owner: "Ilya", arr: "$18,500" },
        { id: "op-4018", account: "Blue Harbor", stage: "Won", owner: "Rae", arr: "$63,200" },
        { id: "op-4021", account: "Arcadia Labs", stage: "Discovery", owner: "Nico", arr: "$11,900" },
      ]}
      selectable
      variant="outline"
      pageSize={4}
    />
  )
}

Server Data Pattern

For large datasets, fetch and paginate on the server, then pass only the current page to `DataTable`. Use this pattern when your API owns filtering, sorting, and pagination.

"use client"

import * as React from "react"
import { DataTable } from "@glinui/ui"

const columns = [
  { id: "account", header: "Account", accessor: "account", sortable: true },
  { id: "stage", header: "Stage", accessor: "stage", sortable: true },
  { id: "arr", header: "ARR", accessor: "arr", sortable: true, align: "right" }
]

export function DataTableServerPattern() {
  const [rows, setRows] = React.useState([])
  const [query, setQuery] = React.useState("")
  const [page, setPage] = React.useState(1)

  React.useEffect(() => {
    const params = new URLSearchParams({ q: query, page: String(page) })
    fetch(`/api/pipeline?${params.toString()}`)
      .then((res) => res.json())
      .then((payload) => setRows(payload.items))
  }, [query, page])

  return (
    <DataTable
      columns={columns}
      data={rows}
      searchable={false}
      pageSize={20}
      variant="glass"
      emptyMessage="No matching records."
    />
  )
}

Accessibility

  • Built on semantic table primitives for screen reader compatibility.
  • Sorting controls are keyboard-focusable buttons in header cells.
  • Selection checkboxes provide explicit labels.
  • Built on semantic table elements (`table`, `thead`, `tbody`, `th`, and `td`) for screen-reader compatibility.
  • Keep column names concise and unique so assistive tech announces context clearly.

Keyboard Interactions

KeyAction
TabMove across toolbar, headers, and pagination controls.
Enter / SpaceActivate sort, selection, and pagination controls.

ARIA Attributes

  • `aria-label` for search and selection controls
  • `role="checkbox"` for selectable rows
  • Native table semantics from the base table primitive

Reduced Motion

Inherits reduced-motion behavior from Table and action/input primitives.

DataTable inherits reduced-motion behavior from `Table`, `Input`, `Select`, and `Button`. Hover/focus transitions are non-essential and respect user preference settings.

Affected properties

background-coloropacity

API Reference

PropTypeRequiredDefaultDescription
columnsDataTableColumn<TData>[]Yes-Column schema with accessor, sorting, visibility, and custom cell rendering.
dataTData[]Yes-Array of row objects.
searchablebooleanNotrueEnable built-in text filtering.
selectablebooleanNofalseAdds row-selection checkboxes.
pageSizenumberNo10Initial page size.
pageSizeOptionsnumber[]No[10, 20, 50]Page-size choices shown in toolbar.
variant"glinr" | "solid" | "plain" | "soft" | "outline" | "ghost" | "gradient" | "glass"-ambient (glinr)Surface variant. Omit to follow the ambient design style (glinr by default, plain for minimal, glass for glass).
classNamestringNo-Auto-generated from TypeScript source.
emptyMessagestringNo"No results."Auto-generated from TypeScript source.
getRowId(row: TData, index: number) => stringNo-Auto-generated from TypeScript source.
onRowClick(row: TData) => voidNo-Auto-generated from TypeScript source.
onSelectionChange(rows: TData[]) => voidNo-Auto-generated from TypeScript source.
rowTone(row: TData) => TableRowToneNo-Auto-generated from TypeScript source.
searchPlaceholderstringNo"Search rows..."Auto-generated from TypeScript source.

Source

Import directly from the package or browse the source on GitHub. Click any file to view it.

Import

import { DataTable } from "@glinui/ui"

Dependencies

@glinui/ui

Generated API Snapshot

Beta

Auto-extracted from TypeScript source in packages/ui/src/components/data-table.tsx. This section is in beta and may lag behind hand-curated docs. Regenerate with pnpm --filter @glinui/docs api:generate.

Generated: 2026-10-08T01:41:21.546Z · Full index: /docs/api-metadata

Primary Props Type

DataTableProps

DataTableProps

PropTypeRequiredDefaultDescription
classNamestringNo--
columnsDataTableColumn<TData>[]Yes--
dataTData[]Yes--
emptyMessagestringNo"No results."-
getRowId(row: TData, index: number) => stringNo--
onRowClick(row: TData) => voidNo--
onSelectionChange(rows: TData[]) => voidNo--
pageSizenumberNo10-
pageSizeOptionsnumber[]No[10, 20, 50]-
rowTone(row: TData) => TableRowToneNo--
searchablebooleanNotrue-
searchPlaceholderstringNo"Search rows..."-
selectablebooleanNofalse-