Strategies
Choose between server-first and local-first data-table loading strategies.
Data-table strategies keep table configuration separate from loading behavior.
Use strategies from the client entrypoint:
import {
createTableConfig,
TableConfigProvider,
useInfiniteStrategy,
useLocalStrategy,
usePaginationStrategy,
} from "@tulip-systems/data-tables/client";Local strategy
Use useLocalStrategy() when rows already live in client state or a local collection and the table only needs configuration, sorting state, command integration, and rendering.
"use client";
import type { TableColumnDef } from "@tulip-systems/data-tables";
import {
createTableConfig,
TableConfigProvider,
useLocalStrategy,
} from "@tulip-systems/data-tables/client";
function CustomerLocalTableProvider({
data,
columns,
children,
}: {
data: { id: string; name: string }[];
columns: TableColumnDef<{ id: string; name: string }>[];
children: React.ReactNode;
}) {
const strategy = useLocalStrategy({ total: data.length });
const config = createTableConfig({
queryData: data,
columns,
getRowId: (row) => row.id,
strategy,
});
return <TableConfigProvider config={config}>{children}</TableConfigProvider>;
}Server-first strategies
Use pagination or infinite strategies when the server remains the source of truth for the current result set.
- pagination works best for admin screens with explicit pages and totals
- infinite loading works best for feed-like or browser-like screens
- local works best when the working set is already loaded or managed by TanStack DB
Keep Query Strategy-Agnostic
Define filters with defineFilters(...) and descriptor builders. The table strategy decides how rows are loaded, but the query definition should stay portable.
import { defineFilters, stringFilter } from "@tulip-systems/query";
export const filters = defineFilters({
search: stringFilter.build((value) =>
value ? { field: "search", operator: "ilike", value } : null,
),
});This lets the same module support a suspense query, a paginated table, an infinite table, or a local on-demand collection without creating a second filter API.