Data Table
Configurable data table with optional column freeze (left/right), customizable header and body colors and backgrounds, optional borders, and optional sortable columns.
UniqueUI CLI
npx uniqueui add data-table
shadcn CLI
npx shadcn@latest add https://uniqueui.com/r/data-table.json -y
shadcn path expects @/lib/utils (run shadcn init first). Same source file is installed to components/ui/.
Default
Variant 1 of 8| Name | Role | Department | Location | Status | Joined | |
|---|---|---|---|---|---|---|
| Alex Kim | Engineer | alex@example.com | Platform | San Francisco | Active | 2023-01 |
| Sara Chen | Designer | sara@example.com | Product | New York | Active | 2023-03 |
| Jordan Lee | PM | jordan@example.com | Growth | Chicago | Away | 2023-06 |
| Maya Patel | Engineer | maya@example.com | Platform | Seattle | Active | 2024-01 |
| Ryan Wu | Designer | ryan@example.com | Product | Austin | Away | 2024-02 |
import { DataTable } from "@/components/ui/data-table";
const columns = [
{ key: "name", label: "Name" },
{ key: "role", label: "Role" },
{ key: "email", label: "Email" },
{ key: "department", label: "Department" },
{ key: "status", label: "Status" },
{ key: "joined", label: "Joined" },
];
const data = [
{ name: "Alex Kim", role: "Engineer", email: "alex@example.com", department: "Platform", status: "Active", joined: "2023-01" },
{ name: "Sara Chen", role: "Designer", email: "sara@example.com", department: "Product", status: "Active", joined: "2023-03" },
{ name: "Jordan Lee", role: "PM", email: "jordan@example.com", department: "Growth", status: "Away", joined: "2023-06" },
{ name: "Maya Patel", role: "Engineer", email: "maya@example.com", department: "Platform", status: "Active", joined: "2024-01" },
{ name: "Ryan Wu", role: "Designer", email: "ryan@example.com", department: "Product", status: "Away", joined: "2024-02" },
{ name: "Priya Shah", role: "PM", email: "priya@example.com", department: "Growth", status: "Active", joined: "2024-04" },
{ name: "Sam Rivera", role: "Engineer", email: "sam@example.com", department: "Platform", status: "Active", joined: "2024-05" },
{ name: "Jess Taylor", role: "Designer", email: "jess@example.com", department: "Product", status: "Active", joined: "2024-06" },
];
export default function Example() {
return (
<div className="w-full p-6">
<DataTable columns={columns} data={data} paginated pageSize={5} theme="dark" />
</div>
);
}Freeze left
Variant 2 of 8| ID | Name | Role | Department | Region | Location | Project | Division | Site | TZ | Cost ctr | Joined | Status | Actions | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1 | Alex Kim | alex@example.com | Engineer | Platform | West | San Francisco | Orion | Platform core | SF-01 | PT | CC-4100 | 2023-01 | Active | Edit · View |
| 2 | Sara Chen | sara@example.com | Designer | Product | East | New York | Nova | Product design | NYC-04 | ET | CC-2200 | 2023-03 | Active | Edit · View |
| 3 | Jordan Lee | jordan@example.com | PM | Growth | Central | Chicago | Atlas | Growth GTM | CHI-02 | CT | CC-1800 | 2023-06 | Away | Edit · View |
| 4 | Maya Patel | maya@example.com | Engineer | Platform | West | Seattle | Pulse | Platform core | SEA-01 | PT | CC-4100 | 2024-01 | Active | Edit · View |
| 5 | Ryan Wu | ryan@example.com | Designer | Product | East | Austin | Vertex | Product design | AUS-01 | CT | CC-3300 | 2024-02 | Away | Edit · View |
import { DataTable } from "@/components/ui/data-table";
const columns = [
{ key: "id", label: "ID" },
{ key: "name", label: "Name" },
{ key: "email", label: "Email" },
{ key: "role", label: "Role" },
{ key: "department", label: "Department" },
{ key: "region", label: "Region" },
{ key: "joined", label: "Joined" },
{ key: "status", label: "Status" },
{ key: "actions", label: "Actions" },
];
const data = [
{ id: "1", name: "Alex Kim", email: "alex@example.com", role: "Engineer", department: "Platform", region: "West", joined: "2023-01", status: "Active", actions: "Edit · View" },
{ id: "2", name: "Sara Chen", email: "sara@example.com", role: "Designer", department: "Product", region: "East", joined: "2023-03", status: "Active", actions: "Edit · View" },
{ id: "3", name: "Jordan Lee", email: "jordan@example.com", role: "PM", department: "Growth", region: "Central", joined: "2023-06", status: "Away", actions: "Edit · View" },
{ id: "4", name: "Maya Patel", email: "maya@example.com", role: "Engineer", department: "Platform", region: "West", joined: "2024-01", status: "Active", actions: "Edit · View" },
{ id: "5", name: "Ryan Wu", email: "ryan@example.com", role: "Designer", department: "Product", region: "East", joined: "2024-02", status: "Away", actions: "Edit · View" },
{ id: "6", name: "Priya Shah", email: "priya@example.com", role: "PM", department: "Growth", region: "Central", joined: "2024-04", status: "Active", actions: "Edit · View" },
{ id: "7", name: "Sam Rivera", email: "sam@example.com", role: "Engineer", department: "Platform", region: "West", joined: "2024-05", status: "Active", actions: "Edit · View" },
{ id: "8", name: "Jess Taylor", email: "jess@example.com", role: "Designer", department: "Product", region: "East", joined: "2024-06", status: "Active", actions: "Edit · View" },
];
export default function Example() {
return (
<div className="w-full p-6">
<DataTable
columns={columns}
data={data}
freezeColumns="left"
freezeLeftCount={2}
paginated
pageSize={5}
theme="dark"
/>
</div>
);
}Freeze right
Variant 3 of 8| Name | Role | Department | Region | Project | Division | Site | TZ | Cost ctr | Location | Status | Joined | Last active | Actions | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Alex Kim | Engineer | Platform | West | Orion | Platform core | SF-01 | PT | CC-4100 | San Francisco | Active | alex@example.com | 2023-01 | 2024-03 | Edit · View |
| Sara Chen | Designer | Product | East | Nova | Product design | NYC-04 | ET | CC-2200 | New York | Active | sara@example.com | 2023-03 | 2024-03 | Edit · View |
| Jordan Lee | PM | Growth | Central | Atlas | Growth GTM | CHI-02 | CT | CC-1800 | Chicago | Away | jordan@example.com | 2023-06 | 2024-02 | Edit · View |
| Maya Patel | Engineer | Platform | West | Pulse | Platform core | SEA-01 | PT | CC-4100 | Seattle | Active | maya@example.com | 2024-01 | 2024-03 | Edit · View |
| Ryan Wu | Designer | Product | East | Vertex | Product design | AUS-01 | CT | CC-3300 | Austin | Away | ryan@example.com | 2024-02 | 2024-01 | Edit · View |
import { DataTable } from "@/components/ui/data-table";
const columns = [
{ key: "name", label: "Name" },
{ key: "role", label: "Role" },
{ key: "department", label: "Department" },
{ key: "region", label: "Region" },
{ key: "status", label: "Status" },
{ key: "email", label: "Email" },
{ key: "joined", label: "Joined" },
{ key: "lastActive", label: "Last active" },
{ key: "actions", label: "Actions" },
];
const data = [
{ name: "Alex Kim", role: "Engineer", department: "Platform", region: "West", status: "Active", email: "alex@example.com", joined: "2023-01", lastActive: "2024-03", actions: "Edit · View" },
{ name: "Sara Chen", role: "Designer", department: "Product", region: "East", status: "Active", email: "sara@example.com", joined: "2023-03", lastActive: "2024-03", actions: "Edit · View" },
{ name: "Jordan Lee", role: "PM", department: "Growth", region: "Central", status: "Away", email: "jordan@example.com", joined: "2023-06", lastActive: "2024-02", actions: "Edit · View" },
{ name: "Maya Patel", role: "Engineer", department: "Platform", region: "West", status: "Active", email: "maya@example.com", joined: "2024-01", lastActive: "2024-03", actions: "Edit · View" },
{ name: "Ryan Wu", role: "Designer", department: "Product", region: "East", status: "Away", email: "ryan@example.com", joined: "2024-02", lastActive: "2024-01", actions: "Edit · View" },
{ name: "Priya Shah", role: "PM", department: "Growth", region: "Central", status: "Active", email: "priya@example.com", joined: "2024-04", lastActive: "2024-03", actions: "Edit · View" },
{ name: "Sam Rivera", role: "Engineer", department: "Platform", region: "West", status: "Active", email: "sam@example.com", joined: "2024-05", lastActive: "2024-03", actions: "Edit · View" },
{ name: "Jess Taylor", role: "Designer", department: "Product", region: "East", status: "Active", email: "jess@example.com", joined: "2024-06", lastActive: "2024-03", actions: "Edit · View" },
];
export default function Example() {
return (
<div className="w-full p-6">
<DataTable
columns={columns}
data={data}
freezeColumns="right"
freezeRightCount={1}
paginated
pageSize={5}
theme="dark"
/>
</div>
);
}Freeze both
Variant 4 of 8| ID | Name | Role | Department | Region | Location | Project | Division | Site | TZ | Cost ctr | Joined | Status | Actions | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1 | Alex Kim | alex@example.com | Engineer | Platform | West | San Francisco | Orion | Platform core | SF-01 | PT | CC-4100 | 2023-01 | Active | Edit · View |
| 2 | Sara Chen | sara@example.com | Designer | Product | East | New York | Nova | Product design | NYC-04 | ET | CC-2200 | 2023-03 | Active | Edit · View |
| 3 | Jordan Lee | jordan@example.com | PM | Growth | Central | Chicago | Atlas | Growth GTM | CHI-02 | CT | CC-1800 | 2023-06 | Away | Edit · View |
| 4 | Maya Patel | maya@example.com | Engineer | Platform | West | Seattle | Pulse | Platform core | SEA-01 | PT | CC-4100 | 2024-01 | Active | Edit · View |
| 5 | Ryan Wu | ryan@example.com | Designer | Product | East | Austin | Vertex | Product design | AUS-01 | CT | CC-3300 | 2024-02 | Away | Edit · View |
import { DataTable } from "@/components/ui/data-table";
const columns = [
{ key: "id", label: "ID" },
{ key: "name", label: "Name" },
{ key: "email", label: "Email" },
{ key: "role", label: "Role" },
{ key: "department", label: "Department" },
{ key: "region", label: "Region" },
{ key: "joined", label: "Joined" },
{ key: "status", label: "Status" },
{ key: "actions", label: "Actions" },
];
const data = [
{ id: "1", name: "Alex Kim", email: "alex@example.com", role: "Engineer", department: "Platform", region: "West", joined: "2023-01", status: "Active", actions: "Edit · View" },
{ id: "2", name: "Sara Chen", email: "sara@example.com", role: "Designer", department: "Product", region: "East", joined: "2023-03", status: "Active", actions: "Edit · View" },
{ id: "3", name: "Jordan Lee", email: "jordan@example.com", role: "PM", department: "Growth", region: "Central", joined: "2023-06", status: "Away", actions: "Edit · View" },
{ id: "4", name: "Maya Patel", email: "maya@example.com", role: "Engineer", department: "Platform", region: "West", joined: "2024-01", status: "Active", actions: "Edit · View" },
{ id: "5", name: "Ryan Wu", email: "ryan@example.com", role: "Designer", department: "Product", region: "East", joined: "2024-02", status: "Away", actions: "Edit · View" },
{ id: "6", name: "Priya Shah", email: "priya@example.com", role: "PM", department: "Growth", region: "Central", joined: "2024-04", status: "Active", actions: "Edit · View" },
{ id: "7", name: "Sam Rivera", email: "sam@example.com", role: "Engineer", department: "Platform", region: "West", joined: "2024-05", status: "Active", actions: "Edit · View" },
{ id: "8", name: "Jess Taylor", email: "jess@example.com", role: "Designer", department: "Product", region: "East", joined: "2024-06", status: "Active", actions: "Edit · View" },
];
export default function Example() {
return (
<div className="w-full p-6">
<DataTable
columns={columns}
data={data}
freezeColumns="both"
freezeLeftCount={2}
freezeRightCount={1}
paginated
pageSize={5}
theme="dark"
/>
</div>
);
}Bordered
Variant 5 of 8| Name | Role | Department | Location | Status | Joined | |
|---|---|---|---|---|---|---|
| Alex Kim | Engineer | alex@example.com | Platform | San Francisco | Active | 2023-01 |
| Sara Chen | Designer | sara@example.com | Product | New York | Active | 2023-03 |
| Jordan Lee | PM | jordan@example.com | Growth | Chicago | Away | 2023-06 |
| Maya Patel | Engineer | maya@example.com | Platform | Seattle | Active | 2024-01 |
| Ryan Wu | Designer | ryan@example.com | Product | Austin | Away | 2024-02 |
import { DataTable } from "@/components/ui/data-table";
const columns = [
{ key: "name", label: "Name" },
{ key: "role", label: "Role" },
{ key: "email", label: "Email" },
{ key: "department", label: "Department" },
{ key: "status", label: "Status" },
{ key: "joined", label: "Joined" },
];
const data = [
{ name: "Alex Kim", role: "Engineer", email: "alex@example.com", department: "Platform", status: "Active", joined: "2023-01" },
{ name: "Sara Chen", role: "Designer", email: "sara@example.com", department: "Product", status: "Active", joined: "2023-03" },
{ name: "Jordan Lee", role: "PM", email: "jordan@example.com", department: "Growth", status: "Away", joined: "2023-06" },
{ name: "Maya Patel", role: "Engineer", email: "maya@example.com", department: "Platform", status: "Active", joined: "2024-01" },
{ name: "Ryan Wu", role: "Designer", email: "ryan@example.com", department: "Product", status: "Away", joined: "2024-02" },
{ name: "Priya Shah", role: "PM", email: "priya@example.com", department: "Growth", status: "Active", joined: "2024-04" },
{ name: "Sam Rivera", role: "Engineer", email: "sam@example.com", department: "Platform", status: "Active", joined: "2024-05" },
{ name: "Jess Taylor", role: "Designer", email: "jess@example.com", department: "Product", status: "Active", joined: "2024-06" },
];
export default function Example() {
return (
<div className="w-full p-6">
<DataTable columns={columns} data={data} border paginated pageSize={5} theme="dark" />
</div>
);
}Sortable
Variant 6 of 8| Jordan Lee | PM | Growth | San Francisco | 2024-03 | Away |
| Alex Kim | Engineer | Platform | New York | 2024-01 | Active |
| Sara Chen | Designer | Product | Chicago | 2024-02 | Active |
| Maya Patel | Engineer | Platform | Seattle | 2023-11 | Active |
| Ryan Wu | Designer | Product | Austin | 2024-05 | Away |
import { DataTable } from "@/components/ui/data-table";
const columns = [
{ key: "name", label: "Name", sortKey: "name" },
{ key: "role", label: "Role", sortKey: "role" },
{ key: "department", label: "Department", sortKey: "department" },
{ key: "joined", label: "Joined", sortKey: "joined" },
{ key: "status", label: "Status", sortKey: "status" },
];
const data = [
{ name: "Jordan Lee", role: "PM", department: "Growth", joined: "2024-03", status: "Away" },
{ name: "Alex Kim", role: "Engineer", department: "Platform", joined: "2024-01", status: "Active" },
{ name: "Sara Chen", role: "Designer", department: "Product", joined: "2024-02", status: "Active" },
{ name: "Maya Patel", role: "Engineer", department: "Platform", joined: "2023-11", status: "Active" },
{ name: "Ryan Wu", role: "Designer", department: "Product", joined: "2024-05", status: "Away" },
{ name: "Priya Shah", role: "PM", department: "Growth", joined: "2023-08", status: "Active" },
{ name: "Sam Rivera", role: "Engineer", department: "Platform", joined: "2024-06", status: "Active" },
{ name: "Jess Taylor", role: "Designer", department: "Product", joined: "2023-09", status: "Active" },
];
export default function Example() {
return (
<div className="w-full p-6">
<DataTable columns={columns} data={data} sortable paginated pageSize={5} theme="dark" />
</div>
);
}Custom colors
Variant 7 of 8| Name | Role | Department | Location | Status | Joined | |
|---|---|---|---|---|---|---|
| Alex Kim | Engineer | alex@example.com | Platform | San Francisco | Active | 2023-01 |
| Sara Chen | Designer | sara@example.com | Product | New York | Active | 2023-03 |
| Jordan Lee | PM | jordan@example.com | Growth | Chicago | Away | 2023-06 |
| Maya Patel | Engineer | maya@example.com | Platform | Seattle | Active | 2024-01 |
| Ryan Wu | Designer | ryan@example.com | Product | Austin | Away | 2024-02 |
import { DataTable } from "@/components/ui/data-table";
const columns = [
{ key: "name", label: "Name" },
{ key: "role", label: "Role" },
{ key: "email", label: "Email" },
{ key: "department", label: "Department" },
{ key: "status", label: "Status" },
{ key: "joined", label: "Joined" },
];
const data = [
{ name: "Alex Kim", role: "Engineer", email: "alex@example.com", department: "Platform", status: "Active", joined: "2023-01" },
{ name: "Sara Chen", role: "Designer", email: "sara@example.com", department: "Product", status: "Active", joined: "2023-03" },
{ name: "Jordan Lee", role: "PM", email: "jordan@example.com", department: "Growth", status: "Away", joined: "2023-06" },
{ name: "Maya Patel", role: "Engineer", email: "maya@example.com", department: "Platform", status: "Active", joined: "2024-01" },
{ name: "Ryan Wu", role: "Designer", email: "ryan@example.com", department: "Product", status: "Away", joined: "2024-02" },
{ name: "Priya Shah", role: "PM", email: "priya@example.com", department: "Growth", status: "Active", joined: "2024-04" },
{ name: "Sam Rivera", role: "Engineer", email: "sam@example.com", department: "Platform", status: "Active", joined: "2024-05" },
{ name: "Jess Taylor", role: "Designer", email: "jess@example.com", department: "Product", status: "Active", joined: "2024-06" },
];
export default function Example() {
return (
<div className="w-full p-6">
<DataTable
columns={columns}
data={data}
headerTextColor="text-purple-900"
bodyTextColor="text-neutral-800"
headerBackground="bg-purple-100"
bodyBackground="bg-purple-50/50"
paginated
pageSize={5}
border
theme="dark"
/>
</div>
);
}Full options
Variant 8 of 8| ID | Dept | Region | Location | Project | Division | Site | TZ | Cost ctr | Joined | Actions | ||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1 | Jordan Lee | PM | Growth | Central | San Francisco | Orion | Platform core | SF-01 | PT | CC-4100 | 2023-06 | Edit |
| 2 | Alex Kim | Engineer | Platform | West | New York | Nova | Product design | NYC-04 | ET | CC-2200 | 2023-01 | Edit |
| 3 | Sara Chen | Designer | Product | East | Chicago | Atlas | Growth GTM | CHI-02 | CT | CC-1800 | 2023-03 | Edit |
| 4 | Maya Patel | Engineer | Platform | West | Seattle | Pulse | Platform core | SEA-01 | PT | CC-4100 | 2024-01 | Edit |
| 5 | Ryan Wu | Designer | Product | East | Austin | Vertex | Product design | AUS-01 | CT | CC-3300 | 2024-02 | Edit |
import { DataTable } from "@/components/ui/data-table";
const columns = [
{ key: "id", label: "ID" },
{ key: "name", label: "Name", sortKey: "name" },
{ key: "role", label: "Role", sortKey: "role" },
{ key: "department", label: "Dept" },
{ key: "region", label: "Region" },
{ key: "joined", label: "Joined" },
{ key: "actions", label: "Actions" },
];
const data = [
{ id: "1", name: "Jordan Lee", role: "PM", department: "Growth", region: "Central", joined: "2023-06", actions: "Edit" },
{ id: "2", name: "Alex Kim", role: "Engineer", department: "Platform", region: "West", joined: "2023-01", actions: "Edit" },
{ id: "3", name: "Sara Chen", role: "Designer", department: "Product", region: "East", joined: "2023-03", actions: "Edit" },
{ id: "4", name: "Maya Patel", role: "Engineer", department: "Platform", region: "West", joined: "2024-01", actions: "Edit" },
{ id: "5", name: "Ryan Wu", role: "Designer", department: "Product", region: "East", joined: "2024-02", actions: "Edit" },
{ id: "6", name: "Priya Shah", role: "PM", department: "Growth", region: "Central", joined: "2024-04", actions: "Edit" },
{ id: "7", name: "Sam Rivera", role: "Engineer", department: "Platform", region: "West", joined: "2024-05", actions: "Edit" },
{ id: "8", name: "Jess Taylor", role: "Designer", department: "Product", region: "East", joined: "2024-06", actions: "Edit" },
];
export default function Example() {
return (
<div className="w-full p-6">
<DataTable
columns={columns}
data={data}
freezeColumns="left"
freezeLeftCount={1}
headerTextColor="text-neutral-100"
bodyTextColor="text-neutral-300"
headerBackground="bg-neutral-800"
bodyBackground="bg-neutral-950"
border
sortable
paginated
pageSize={5}
pageSizeOptions={[5, 10, 20]}
onPageChange={(page, pageSize) =>
console.log("page changed", page, "pageSize", pageSize)
}
theme="dark"
/>
</div>
);
}Props
| Prop | Type | Description |
|---|---|---|
columns | DataTableColumn[] | Column definitions: key, label, and optional sortKey for sortable columns. |
data | Record<string, React.ReactNode>[] | Row data; each row is an object keyed by column key. |
freezeColumns | "none" | "left" | "right" | "both" | Which columns to freeze when horizontally scrolling: none, left, right, or both sides. |
freezeCount | number | Legacy fallback count used only when freezeLeftCount or freezeRightCount are not provided. |
freezeLeftCount | number | How many columns to freeze on the left when freezeColumns is "left" or "both". |
freezeRightCount | number | How many columns to freeze on the right when freezeColumns is "right" or "both". |
paginated | boolean | Enable built-in pagination controls. |
pageSize | number | Number of rows per page when paginated is true. |
pageSizeOptions | number[] | Optional list of page sizes to show in a selector. |
initialPage | number | Initial page index (1-based) when paginated is true. |
onPageChange | (page: number, pageSize: number) => void | Called when the current page or page size changes. |
paginationPreviousLabel | React.ReactNode | Previous-page control content; defaults to a left chevron icon. |
paginationNextLabel | React.ReactNode | Next-page control content; defaults to a right chevron icon. |
getRowKey | (row, index) => React.Key | Optional stable key per row for React reconciliation (sort, pagination). Defaults to row.id / row.key when string or number, else a generated key. |
headerTextColor | string | Tailwind class for header text, e.g. text-neutral-900. |
bodyTextColor | string | Tailwind class for body cell text. |
headerBackground | string | Tailwind class for header background, e.g. bg-neutral-100. |
bodyBackground | string | Tailwind class for body background. |
border | boolean | Whether to show table and cell borders. |
sortable | boolean | Whether column headers with sortKey are clickable to sort. |
onSort | (key: string, direction: "asc" | "desc") => void | Callback when sort changes (for controlled use). |
className | string | Additional classes on the root wrapper. |
theme | "light" | "dark" | Theme for default header/body colors when not overridden. |