gridcn1.2.2

SWR

Lazy ranges through SWR's preload. One fetch per range key, shared with any useSWR consumer.

SWR has no queryClient.fetchQuery. preload(key, fetcher) is its imperative entry point. It runs the fetcher once per key, hands back its promise, and stores the result in the global cache. So a second grid or a detail panel on the same page that calls useSWR with the same key gets the range for free.

0 / 10,000 rows loadedeach window cached globally by SWR
ID
Name
Email
Score
pnpm dlx shadcn@latest add @gridcn/data-grid-lazy-swr-demo

The seam

const lazy = useDataGridLazyRows<Row>({
  total: 10_000,
  getRowId: (row) => row.id,
  fetchRows: (start, end, signal) =>
    preload(["rows", start, end] as [string, number, number], (key) =>
      api.fetchRows(key[1], key[2], signal),
    ),
});

The fetcher receives the key itself as its argument (the SWR 2.x convention. useSWR works the same way), hence the tuple cast rather than destructured parameters. Concurrent requests for the same range share one fetch, and the cached range survives for the session. Scrolling back never refetches.

Revalidate on focus

useSWR components revalidate on window focus and reconnect by default. The lazy ranges here are plain cache entries consumed through the hook, so they stay put for the session ( no focus-time churn while you scroll). If you also mount useSWR consumers on the same keys, decide once whether you want that focus revalidation (revalidateOnFocus: false) or rely on it as a cheap freshness check.

Behavior reference

On this page