react-server675fbba4
react-servertreemaindocssrcpagesen(pages)integrationsreact-query.mdx
docs/src/pages/en/(pages)/integrations/react-query.mdxmdx6.4 KiBca8fdcde

title: TanStack Query category: Integrations order: 4

import Link from "../../../../components/Link.jsx";

TanStack Query

@lazarv/react-server works with TanStack Query (formerly React Query) to provide powerful data fetching with server-side prefetching and client-side hydration. You can prefetch queries in server components and seamlessly hydrate the data on the client, avoiding unnecessary re-fetches.

<Link name="installation"> ## Installation </Link>

Install TanStack Query in your project:

pnpm add @tanstack/react-query
<Link name="setup"> ## Setup </Link>

To use TanStack Query, you need to create a QueryClient and wrap your app in a QueryClientProvider. Since QueryClientProvider relies on React context, it must be a client component.

Create a query client factory that handles both server and browser environments:

import {
  defaultShouldDehydrateQuery,
  isServer,
  QueryClient,
} from "@tanstack/react-query";

function makeQueryClient() {
  return new QueryClient({
    defaultOptions: {
      queries: {
        // With SSR, set some default staleTime above 0
        // to avoid refetching immediately on the client
        staleTime: 60 * 1000,
      },
      dehydrate: {
        // include pending queries in dehydration
        shouldDehydrateQuery: (query) =>
          defaultShouldDehydrateQuery(query) ||
          query.state.status === "pending",
      },
    },
  });
}

let browserQueryClient = undefined;

export function getQueryClient() {
  if (isServer) {
    // Server: always make a new query client
    return makeQueryClient();
  } else {
    // Browser: make a new query client if we don't already have one
    if (!browserQueryClient) browserQueryClient = makeQueryClient();
    return browserQueryClient;
  }
}

Create a client component that provides the QueryClient to the rest of your app:

"use client";

import {
  isServer,
  QueryClient,
  QueryClientProvider,
} from "@tanstack/react-query";

function makeQueryClient() {
  return new QueryClient({
    defaultOptions: {
      queries: {
        staleTime: 60 * 1000,
      },
    },
  });
}

let browserQueryClient = undefined;

function getQueryClient() {
  if (isServer) {
    return makeQueryClient();
  } else {
    if (!browserQueryClient) browserQueryClient = makeQueryClient();
    return browserQueryClient;
  }
}

export default function Providers({ children }) {
  const queryClient = getQueryClient();

  return (
    <QueryClientProvider client={queryClient}>
      {children}
    </QueryClientProvider>
  );
}

Then wrap your app in the Providers component from your root layout:

import Providers from "./providers";

export default function RootLayout({ children }) {
  return (
    <html lang="en" suppressHydrationWarning>
      <head />
      <body suppressHydrationWarning>
        <Providers>{children}</Providers>
      </body>
    </html>
  );
}
<Link name="server-side-prefetching"> ## Server-side prefetching </Link>

The key advantage of using TanStack Query with @lazarv/react-server is the ability to prefetch data in server components and hydrate it on the client. This means data is available instantly without a loading state.

In a server component, use the query client to prefetch data and wrap your client components in a HydrationBoundary:

import { dehydrate, HydrationBoundary } from "@tanstack/react-query";

import { getPosts } from "./get-posts";
import { getQueryClient } from "./get-query-client";
import Posts from "./posts";

export default function PostsPage() {
  const queryClient = getQueryClient();

  queryClient.prefetchQuery({
    queryKey: ["posts"],
    queryFn: getPosts,
  });

  return (
    <HydrationBoundary state={dehydrate(queryClient)}>
      <Posts />
    </HydrationBoundary>
  );
}
<Link name="client-components"> ## Client components </Link>

Client components can use useSuspenseQuery (or useQuery) with the same query keys. When the data was prefetched on the server, it will be immediately available without a loading state:

"use client";

import { useSuspenseQuery } from "@tanstack/react-query";
import { getPosts } from "./get-posts";

export default function Posts() {
  const { data } = useSuspenseQuery({
    queryKey: ["posts"],
    queryFn: getPosts,
  });

  return (
    <ul>
      {data.map((post) => (
        <li key={post.id}>{post.title}</li>
      ))}
    </ul>
  );
}
<Link name="isomorphic-data-fetching"> ## Isomorphic data fetching </Link>

To share data fetching logic between server and client, you can create isomorphic data fetchers that detect the runtime environment:

export async function getPosts() {
  if (typeof document === "undefined") {
    // Server: import data directly
    const { default: posts } = await import("../data/posts.json");
    return posts;
  } else {
    // Client: fetch from API route
    const res = await fetch("/api/posts");
    return res.json();
  }
}

You can combine this with API routes using file-system routing:

import posts from "../../data/posts.json";

export default async function GET() {
  return new Response(JSON.stringify(posts), {
    status: 200,
    headers: {
      "Content-Type": "application/json",
    },
  });
}
<Link name="nested-hydration"> ## Nested hydration boundaries </Link>

You can nest HydrationBoundary components in separate server components to prefetch different sets of data. This is useful for composing multiple data dependencies:

import { dehydrate, HydrationBoundary } from "@tanstack/react-query";

import Comments from "./comments";
import { getComments } from "./get-comments";
import { getQueryClient } from "./get-query-client";

export default function CommentsServerComponent() {
  const queryClient = getQueryClient();

  queryClient.prefetchQuery({
    queryKey: ["comments"],
    queryFn: getComments,
  });

  return (
    <HydrationBoundary state={dehydrate(queryClient)}>
      <Comments />
    </HydrationBoundary>
  );
}

Check out the TanStack Query example to see a complete example of using TanStack Query with @lazarv/react-server.