DataTable
DataTable
The DataTable component is a powerful, production-ready table that displays your data with built-in sorting, filtering, pagination, and selection. It's built on top of @tanstack/react-table and designed to work seamlessly with Better Query.
What you'll use it for: Displaying lists of records (users, posts, products, etc.) in a professional table format with features users expect like sorting columns, searching, and pagination.
Key Features
- ✅ Sorting - Click column headers to sort data
- ✅ Filtering - Built-in search and filter capabilities
- ✅ Pagination - Automatic pagination with page controls
- ✅ Row Selection - Select single or multiple rows
- ✅ Responsive - Works on mobile and desktop
- ✅ Customizable - Full control over columns and rendering
- ✅ Type-Safe - Full TypeScript support
Usage
Basic Example
Here's the simplest way to use DataTable:
import { List, DataTable } from '@/components/admin';
export const PostList = () => (
<List resource="posts">
<DataTable>
<DataTable.Col source="id" />
<DataTable.Col source="title" />
<DataTable.Col source="status" />
<DataTable.Col source="created_at" />
</DataTable>
</List>
);What this creates:
- A table with 4 columns
- Automatic data fetching from the "posts" resource
- Default loading and error states
What is source? It's the field name from your data. If your posts have a title field, use source="title" to display it.
With Better Query
For more control, use Better Query directly:
import { useQuery } from 'better-admin';
import { query } from '@/lib/query';
import { DataTable } from '@/components/admin/data-table';
export function UsersList() {
// Fetch users from database
const { list } = useQuery("user", query);
const { data, isLoading } = list.useQuery();
if (isLoading) return <div>Loading...</div>;
// Define columns
const columns = [
{ accessorKey: "name", header: "Name" },
{ accessorKey: "email", header: "Email" },
{ accessorKey: "role", header: "Role" },
];
return <DataTable columns={columns} data={data || []} />;
}Why use this approach?
- More control over data fetching
- Easier to add custom loading states
- Can transform data before displaying
Components
DataTable.Col
Defines a column in the table. Each column displays one field from your data.
<DataTable.Col
source="title" // Field name from your data
label="Post Title" // Column header text
sortable // Enable sorting on this column
render={(value, record) => (
<Link to={`/posts/${record.id}`}>{value}</Link>
)}
/>Props
| Prop | Type | Required | Description |
|---|---|---|---|
source | string | Yes | Field name from the data object |
label | string | No | Column header text (defaults to source) |
sortable | boolean | No | Enable sorting for this column |
render | (value, record) => ReactNode | No | Custom renderer for cell content |
width | string | number | No | Column width (e.g., "200px" or 200) |
Custom rendering: The render function receives two arguments:
value- The value of this fieldrecord- The complete data object for this row
This lets you access other fields when rendering. For example, you might show a user's name but link to their full profile.
Examples
Basic Table
import { List, DataTable } from '@/components/admin';
export const PostList = () => (
<List resource="posts">
<DataTable>
<DataTable.Col source="id" label="ID" />
<DataTable.Col source="title" label="Title" />
<DataTable.Col source="status" label="Status" />
<DataTable.Col source="created_at" label="Created" />
</DataTable>
</List>
);With Custom Renderers
import { List, DataTable, EditButton } from '@/components/admin';
import { Badge } from '@/components/ui/badge';
export const PostList = () => (
<List resource="posts">
<DataTable>
<DataTable.Col source="title" />
<DataTable.Col
source="status"
render={(status) => (
<Badge variant={status === 'published' ? 'default' : 'secondary'}>
{status}
</Badge>
)}
/>
<DataTable.Col
source="author"
render={(author) => (
<div className="flex items-center gap-2">
<Avatar src={author.avatar} />
<span>{author.name}</span>
</div>
)}
/>
<DataTable.Col
label="Actions"
render={(_, record) => (
<div className="flex gap-2">
<EditButton record={record} />
<Button variant="outline" size="sm">View</Button>
</div>
)}
/>
</DataTable>
</List>
);With Sorting
import { List, DataTable } from '@/components/admin';
export const PostList = () => (
<List resource="posts">
<DataTable>
<DataTable.Col source="title" sortable />
<DataTable.Col source="created_at" sortable />
<DataTable.Col source="view_count" sortable />
</DataTable>
</List>
);With Selection
import { List, DataTable } from '@/components/admin';
export const PostList = () => (
<List resource="posts">
<DataTable selectable>
<DataTable.Col source="title" />
<DataTable.Col source="status" />
</DataTable>
</List>
);Features
Sorting
Columns can be made sortable by adding the sortable prop:
<DataTable.Col source="title" sortable />Pagination
Pagination is automatically handled based on the data from the List component.
Selection
Enable row selection with the selectable prop:
<DataTable selectable>
{/* columns */}
</DataTable>Loading States
The DataTable automatically shows loading states when data is being fetched.
Empty States
Custom empty state when no data is available:
<DataTable emptyState={<div>No posts found</div>}>
{/* columns */}
</DataTable>Props
| Prop | Type | Description |
|---|---|---|
children | ReactNode | DataTable.Col components |
selectable | boolean | Enable row selection |
onSelectionChange | (selection) => void | Selection change handler |
emptyState | ReactNode | Custom empty state |
Installation
npx better-admin add data-tableRelated
- List - List container
- EditButton - Edit record button
- CreateButton - Create record button