Written by
amitkys
At
Tue Aug 04 2026
Tanstack Query Implementation in Nextjs
Implementation
Query Provider
Data can we exposed via query client from one user to another. To prevent that ... Create a file named query-provider.tsx and
add the following code.
"use client";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { ReactNode } from "react";
// Create a new QueryClient
function makeQueryClient() {
return new QueryClient({
defaultOptions: {
queries: {
// Don't refetch immediately after hydration
staleTime: 60 * 1000,
},
},
});
}
// Reuse one QueryClient in the browser
let browserQueryClient: QueryClient | undefined;
function getQueryClient() {
// Server: create a new QueryClient for every request
if (typeof window === "undefined") {
return makeQueryClient();
}
// Browser: reuse the same QueryClient
if (!browserQueryClient) {
browserQueryClient = makeQueryClient();
}
return browserQueryClient;
}
export default function QueryProvider({
children,
}: {
children: ReactNode;
}) {
return (
<QueryClientProvider client={getQueryClient()}>
{children}
</QueryClientProvider>
);
}Update Layout.tsx file
import "./globals.css";
import { Providers } from "@/components/query-provider"
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html
lang="en"
className={`${geistSans.variable} ${geistMono.variable} h-full antialiased`}
>
<body className="min-h-full flex flex-col">
<Providers>{children}</Providers>
</body>
</html>
);
}Files Structure
we are gonna have mainly 2 files, one for GET data, another for.. if we mutate data on Server
like, update, create, delete (this all come in data modification), so this operation goes to mut-post.tsx
get-post.tsx
mut-post.tsx
Create the following files & routes:
import { queryOptions, useQuery } from "@tanstack/react-query";
import axios from "axios";
interface Post {
userId: number;
id: number;
title: string;
body: string;
}
export function getPostsQuery() {
return queryOptions({
queryKey: ["get-posts"],
queryFn: async () => {
try {
const { data } = await axios.get<Post>("https://jsonplaceholder.typicode.com/posts/1");
return data;
}
catch (e) {
if (axios.isAxiosError(e)) {
console.error("axios error", e.response?.status);
console.error("response", e.response?.data)
console.error("message", e.message)
}
throw e;
}
}
});
}
export function useGetPostsQuery() {
return useQuery(getPostsQuery())
}data fetching on client side
"use client";
import { useGetPostsQuery } from "./query/get-post";
export default function Page() {
const { isPending, isError, data, error } = useGetPostsQuery();
if (isPending) {
return (
<div>Loading...</div>
)
}
if (isError) {
return (
<div> Error: {error?.message}</div>
)
}
if (!data) {
return <p>data not found</p>
}
return (
<div> {data.title} </div>
)
}data fetching on server side
import { HydrationBoundary, QueryClient, dehydrate } from "@tanstack/react-query"
import { getPostsQuery } from "./query/get-post"
import { ClientComponent } from "./_components/client-component"
export default async function Page() {
const queryClient = new QueryClient();
await queryClient.prefetchQuery(getPostsQuery());
return (
<HydrationBoundary state={dehydrate(queryClient)}>
<ClientComponent />
</HydrationBoundary>
)
}