MonoStack
Concepts

Remote Data Grid

The remote variant fetches data asynchronously via a query function instead of receiving pre-loaded data. Import from @repo/table/remote.

Basic Usage

<script lang="ts">
  import { createColumnHelper, DataGrid, useDataGrid } from '@repo/table/remote'
  import { getUsers } from './users.remote'

  const ch = createColumnHelper<User>()

  const { table, dataGrid } = useDataGrid({
    tableOptions: {
      columns: ch.columns([
        ch.accessor('name', { header: 'Name' }),
        ch.accessor('email', { header: 'Email' }),
      ]),
    },
    query: getUsers,
    getRowLink: (row) => `/users/${row.id}`,
  })
</script>

<DataGrid.Root {dataGrid} {table} title="Users" add="/users/add" />

useDataGrid returns the table and data-grid state synchronously. The remote query itself updates reactively.

RemoteDataGridOptions

OptionTypeDescription
tableOptionsDataGridTableOptions<T>Feature-bound TanStack Table config
queryRemoteQueryFunction<OverviewQueryParams<F>, PaginatedResult<T>>Async function that fetches data
getRowLink(row: Row<T>) => string?Row link generator
filterSchemaz.ZodObject?Zod schema for filters

Query Function

The query function receives OverviewQueryParams:

interface OverviewQueryParams<F> {
  filters?: z.infer<F>
  sorting: SortingState
  pagination: PaginationState
}

Must return PaginatedResult<T> ({ data: T[], count: number }).

DataGrid Return Value

The remote dataGrid object exposes reactive getters and setters backed by TanStack atoms:

PropertyTypeDescription
paginationResultCountnumberFiltered result count
paginationTotalCountnumberUnfiltered result count
getRowLinkGetRowLink<T>Row link function
filtersz.infer<F>Current filter state
setFilters(value?) => voidUpdate filters and reload
sortingSortingStateCurrent sort state
setSorting(state) => voidUpdate sorting and reload
paginationPaginationStateCurrent pagination state
setPagination(state) => voidUpdate pagination and reload
loadingbooleanWhether data is being fetched
refresh() => voidRe-fetch with current params

Remote Filters

Use DataGrid.FilterForm inside DataGrid.Root children:

<script lang="ts">
  import { DataGrid, useDataGrid } from '@repo/table/remote'
  import { z } from 'zod/v4'

  const filterSchema = z.object({
    name: z.string().optional(),
    status: z.enum(['active', 'inactive']).optional(),
  })

  const { table, dataGrid } = useDataGrid({
    tableOptions: { columns },
    query: getUsers,
    filterSchema,
  })
</script>

<DataGrid.Root {dataGrid} {table} title="Users">
  <DataGrid.FilterForm schema={filterSchema} />
</DataGrid.Root>

FilterForm reads dataGrid from Svelte context set by DataGrid.Root. Filters appear as toolbar badges: text fields apply after a short debounce, while other fields, removals, and resets apply immediately. Set applyFiltersImmediately={false} on DataGrid.Root to stage changes and show an Apply button.

Use DataGrid.FilterField to register custom inputs and badge metadata:

<DataGrid.FilterForm schema={filterSchema}>
  {#snippet children({ fields })}
    <DataGrid.FilterField
      field={fields.status}
      input={TypedForm.SelectInput}
      optionItems={statusOptions}
    />
  {/snippet}
</DataGrid.FilterForm>

createOverviewQuerySchema

Server-side helper to validate query params in your remote function:

import { createOverviewQuerySchema } from '@repo/table/remote'
import { z } from 'zod/v4'

const filterSchema = z.object({
  name: z.string(),
  status: z.enum(['active', 'inactive']),
})

const querySchema = createOverviewQuerySchema(filterSchema)
// Produces: z.object({
//   pagination: z.object({ pageIndex: z.number().min(0), pageSize: z.number().min(1) }),
//   sorting: z.array(z.object({ id: z.string(), desc: z.boolean() })),
//   filters: filterSchema.partial().optional(),
// })

URL State

The remote data grid uses qs for URL serialization. Params:

  • ?sort=name:ASC
  • ?page=2&pageSize=10
  • ?filter[name]=Alice&filter[status]=active

State updates use SvelteKit shallow replaceState() to update the URL without route navigation.

Type Helper

Extract the data type from a query function:

import type { QueryDataPoint } from '@repo/table/remote'

type User = QueryDataPoint<typeof getUsers>

On this page