Data Display
Data grids with sorting, filtering, pagination, and URL state persistence.
Package: @repo/table Built on: TanStack Table
Basic Data Grid
<script lang="ts">
import type { QueryDataPoint } from '@repo/table/remote'
import { getUsersQuery } from '$lib/domains/users/database/users.remote'
import { userFilterSchema } from '$lib/domains/users/utils/schemas'
import { DataGrid, useDataGrid } from '@repo/table/remote'
import { createColumnHelper } from '@tanstack/table-core'
const ch = createColumnHelper<QueryDataPoint<typeof getUsersQuery>>()
const { table, dataGrid } = await useDataGrid({
tableOptions: {
columns: [
ch.accessor('name', {
header: 'Name',
}),
ch.accessor('createdAt', {
header: 'Created At',
cell: ({ row }) => formatDate(row.original.createdAt, 'HH:mm dd-MM-yyyy'),
}),
],
state: {
sorting: [{ id: 'name', desc: false }],
},
},
query: getUsersQuery,
filterSchema: userFilterSchema,
getRowLink: row => `users/${row.id}`,
})
</script>
<DataGrid.Root title='Users' {table} {dataGrid} add='/users/add'>
<DataGrid.FilterForm schema={userFilterSchema} />
</DataGrid.Root>
Column Configuration
Use createColumnHelper from TanStack Table for type-safe columns:
const ch = createColumnHelper<QueryDataPoint<typeof getUsersQuery>>()
const columns = [
ch.accessor('name', { header: 'Name' }),
ch.accessor('email', { header: 'Email' }),
ch.accessor('createdAt', {
header: 'Joined',
cell: ({ row }) => formatDate(row.original.createdAt, 'dd-MM-yyyy'),
}),
]
Add Button
Pass a URL string or boolean to show an add button:
<!-- Link to add page -->
<DataGrid.Root title='Users' {table} {dataGrid} add='/users/add'>
<!-- Conditional add based on permissions -->
<DataGrid.Root title='Users' {table} {dataGrid} add={await hasAccess([createPermissionGate('users', ['create'])])}>
Server Query Definition
// users.remote.ts
import { query } from '$app/server'
import { createOverviewQuerySchema } from '@repo/table/remote'
import { overviewToQuery, countSelect } from '@repo/db'
export const getUsersQuery = query(
createOverviewQuerySchema(userFilterSchema),
async (data) => {
await validateSession([createPermissionGate('users', ['list'])])
const { db, usersTable } = await getTenantDb()
return await overviewToQuery(
db.select({
id: usersTable.id,
name: usersTable.name,
createdAt: usersTable.createdAt,
count: countSelect,
})
.from(usersTable)
.$dynamic(),
usersTable,
data,
)
},
)
Full Documentation
See @repo/table for complete API reference.