Getting started with TanStack

Let start with - What is TanStack Query?
Tanstack also know as react query, is a modern library for managing server state in your react/Next js applications. It really shines for its simplification of data fetching , caching, synchronization, and updating, making it an ideal choice for data fetching among developers, Does not matter whether you working with Rest APIs or GraphQL.
Key highlights :-
Effortless Data Fetching: Fetch data with minimal boilerplate code using hooks like
useQueryanduseInfiniteQueryCaching: Caches data to prevent unnecessary network requests and improving performance.
Real-Time Updates: Refetches data once the cached data out of stale time.
Pagination / Load more fetching style: Really simplifies Handling paginated/ Lode More or infinite scrolling data effortlessly. with help of
useInfiniteQueryhook.Error Handling: Built-in mechanisms to handle loading states, errors, and retries with ease.
Background Updates: Keeps your app responsive by refetching data in the background without blocking the UI.
Why I Chose TanStack for My Project
In my marketplace app, products were refetched and reshuffled every time the page loaded, causing frustration when users returned after viewing a product, only to find it missing.
TanStack Query solved this by caching product data, ensuring the list stayed consistent across navigations, improving the user experience and API efficiency.
Enough theory , Lets see it in action
useQuery
Installation
To start using TanStack Query in your React project, you’ll need to install it using npm or yarn.
npm install @tanstack/react-query
Query client setup (React): Initialize QueryClient and wrap you application with QueryClientProvider , to make the query client available throughout your app.
import {
QueryClient,
QueryClientProvider,
useQuery,
} from '@tanstack/react-query'
const queryClient = new QueryClient() // Creating new instance of queryclient
export default function App() {
return (
<QueryClientProvider client={queryClient}> // passing it throught out app
<Example />
</QueryClientProvider>
)
}
function Example() {
const { isPending, error, data } = useQuery({
queryKey: ['repoData'],
queryFn: () =>
fetch('https://api.github.com/repos/TanStack/query').then((res) =>
res.json(),
),
})
if (isPending) return 'Loading...'
if (error) return 'An error has occurred: ' + error.message
return (
<div>
<h1>{data.name}</h1>
<p>{data.description}</p>
<strong>👀 {data.subscribers_count}</strong>{' '}
<strong>✨ {data.stargazers_count}</strong>{' '}
<strong>🍴 {data.forks_count}</strong>
</div>
)
}
queryKey: It's a unique identifier for a query, used by React Query to identify a specific query in the cache. It helps React Query decide whether it needs to refetch the data or retrieve it from the cache. ThequeryKeyis typically an array.queryFn: It's a function that fetches the data. It is called when the query is triggered, such as on the first render or during a refetch.data: Data being returned from the backend
Query client setup (NextJs): For next js you need to wrap your layout with the query client provider like following:
Create providers.tsx file
Create the new instance of QueryClient and pass it to the QueryClientProvider
"use client";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
const Providers = ({ children }: { children: React.ReactNode }) => {
const queryClient = new QueryClient();
return (
<>
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
</>
);
};
export default Providers;
In your layout.tsx :
Wrap the children with the provider you created earlier, so your entire app can use React Query.
import { Inter } from "next/font/google";
import Providers from "@/components/providers/";
const inter = Inter({ subsets: ["latin"] });
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body className={inter.className}>
<Providers>
{children}
</Providers>
</body>
</html>
);
}
further the process is same to fetch the data as it was in react…
useInfiniteQueryFor data fetching when implementing infinite scroll or a "Load More" feature to retrieve additional data from the API, you should use
useInfiniteQuery. This hook provides many useful features for implementing load more or infinite scroll functionality.Main code :
Make sure you have imported
useInfiniteQueryat the top of your fileconst { data, isLoading, isFetching, fetchNextPage, hasNextPage, error } = useInfiniteQuery({ queryKey: ["products"], queryFn: async ({ pageParam = 0 }) => { const response = await fetch( `${process.env.NEXT_PUBLIC_BACKEND_URL}/customer/products?skip=${ pageParam * 10 }&limit=10` ); if (!response.ok) throw new Error("Failed to fetch products"); const data = await response.json(); return { products: data.products, nextPage: pageParam + 1, hasMore: data.products.length === 10 && data.total_count > pageParam * 10, }; }, getNextPageParam: (lastPage) => lastPage.hasMore ? lastPage.nextPage : undefined, staleTime: 1000 * 60 * 20, initialPageParam: 0, });fetchNextPage:This is a function you call when you want to load more data, typically used in infinite scroll or a "Load More" button.
When called, it fetches the next set of results from the API.
hasNextPage:A boolean (
trueorfalse) that tells you if there are more pages of data to load.If
true, you can callfetchNextPage; iffalse, you've reached the end of the results.React Query Sets
hasNextPage:React Query uses the result of
getNextPageParam.If
getNextPageParamreturns a value (likenextPage),hasNextPageis set totrue.If
getNextPageParamreturnsundefined,hasNextPageis set tofalse.
getNextPageParam:A function that figures out the parameter (like
pageParam) for the next batch of data.In this case, it checks
hasMorein the last page and returns the next page number if more data is available.
staleTime:Specifies how long the data stays "fresh" in the cache.
For 20 minutes (
1000 * 60 * 20), the cached data will be used without refetching unless explicitly refreshed.
initialPageParam:The starting page parameter.
Here, it's set to
0, meaning the API starts fetching from the first page (or the first set of products).
{hasNextPage && (
<div className="text-center mt-8">
<button
disabled={isFetching}
onClick={() => fetchNextPage()}
className="border border-[#D4D4D4] text-[#0A0A0A] font-OpenSans font-semibold text-[12px] leading-[16px] sm:text-[14px] sm:leading-[18px] rounded-full py-[12px] px-[16px] "
>
{isFetching ? "Loading..." : "Load More"}
</button>
</div>
)}
So, if hasNextPage is true, we display a "Load More" button. When clicked, it calls the fetchNextPage function provided by useInfiniteQuery to get the next batch of data.
Official docs if you want to learn more:
https://tanstack.com/query/latest/docs/framework/react/overview