Skip to main content

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 { createColumnHelper, DataGrid, useDataGrid } from '@repo/table/remote'

const ch = createColumnHelper<QueryDataPoint<typeof getUsersQuery>>()

const { table, dataGrid } = useDataGrid({
tableOptions: {
columns: ch.columns([
ch.accessor('name', {
header: 'Name',
}),
ch.accessor('createdAt', {
header: 'Created At',
cell: ({ row }) => formatDate(row.original.createdAt, 'HH:mm dd-MM-yyyy'),
}),
]),
initialState: {
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 the feature-bound createColumnHelper exported by @repo/table/remote:

const ch = createColumnHelper<QueryDataPoint<typeof getUsersQuery>>()

const columns = ch.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.