DEV Community

Kb Bohara
Kb Bohara

Posted on

TanStack Query 101 with Next.js

In modern web development, managing server state and data fetching can be a challenge. Enter TanStack Query (formerly known as React Query), a powerful library that simplifies data fetching, caching, synchronization, and more. When paired with Next.js, a popular React framework for server-rendered applications, you can build highly performant and scalable apps with ease.

In this blog post, we’ll walk through the basics of using TanStack Query with Next.js. Whether you're a beginner or an experienced developer, this guide will help you get started with TanStack Query and unlock its full potential.


What is TanStack Query?

TanStack Query is a async state management. It provides tools for:

  • Data fetching: Easily fetch data from APIs.
  • Caching: Automatically cache data to avoid unnecessary network requests.
  • Background updates: Keep your UI up to date with fresh data.
  • Error handling: Simplify error handling and retries.
  • Devtools: Debug and visualize your queries with built-in devtools.

When combined with Next.js, TanStack Query becomes a powerhouse for building server-rendered, statically generated, or client-rendered applications.


Why Use TanStack Query with Next.js?

Next.js is a versatile framework that supports server-side rendering (SSR), static site generation (SSG), and client-side rendering (CSR). TanStack Query complements Next.js by:

  1. Simplifying data fetching: No more manual useEffect hooks or complex state management.
  2. Improving performance: Automatic caching and background updates reduce unnecessary network requests.
  3. Enhancing developer experience: Built-in devtools make debugging a breeze.
  4. Supporting hybrid rendering: Works seamlessly with SSR, SSG, and CSR.

Getting Started: TanStack Query with Next.js

Let’s dive into the practical steps of setting up and using TanStack Query in a Next.js project.


1. Set Up a Next.js Project

If you don’t already have a Next.js project, create one:

npx create-next-app@latest my-tanstack-query-app
cd my-tanstack-query-app
Enter fullscreen mode Exit fullscreen mode

2. Install TanStack Query

Install the required packages:

npm install @tanstack/react-query @tanstack/react-query-devtools
Enter fullscreen mode Exit fullscreen mode
  • @tanstack/react-query: The core library for managing queries.
  • @tanstack/react-query-devtools: A tool for debugging and visualizing queries.

3. Set Up the QueryClient

The QueryClient is the heart of TanStack Query. It manages the cache and provides the context for your queries.

Create a providers.js file in the root of your project:

// providers.js
"use client"; // Mark this as a Client Component

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

const queryClient = new QueryClient();

export default function Providers({ children }) {
  return (
    <QueryClientProvider client={queryClient}>
      {children}
      <ReactQueryDevtools initialIsOpen={false} />
    </QueryClientProvider>
  );
}
Enter fullscreen mode Exit fullscreen mode

4. Wrap Your App with the QueryClientProvider

In your app/layout.js, wrap your application with the Providers component:

// app/layout.js
import Providers from "../providers";

export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body>
        <Providers>{children}</Providers>
      </body>
    </html>
  );
}
Enter fullscreen mode Exit fullscreen mode

5. Fetch Data with useQuery

Now that everything is set up, let’s fetch some data using the useQuery hook.

Create a new page, e.g., app/page.js:

// app/page.js
"use client"; // Mark this as a Client Component

import { useQuery } from "@tanstack/react-query";

async function fetchPosts() {
  const res = await fetch("https://jsonplaceholder.typicode.com/posts");
  if (!res.ok) {
    throw new Error("Failed to fetch posts");
  }
  return res.json();
}

export default function Home() {
  const { data, isLoading, isError, error } = useQuery({
    queryKey: ["posts"],
    queryFn: fetchPosts,
  });

  if (isLoading) return <div>Loading...</div>;
  if (isError) return <div>Error: {error.message}</div>;

  return (
    <div>
      <h1>Posts</h1>
      <ul>
        {data.map((post) => (
          <li key={post.id}>{post.title}</li>
        ))}
      </ul>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

6. Key Concepts

Query Keys

  • Query keys are unique identifiers for your queries. They can be strings or arrays.
  • Example: ["posts", 1] for fetching a specific post.

Query Functions

  • A query function is an asynchronous function that returns data.
  • Example: fetchPosts in the code above.

Query States

  • isLoading: True while the data is being fetched for the first time.
  • isError: True if the query encounters an error.
  • error: Contains the error object if isError is true.
  • data: Contains the fetched data once the query is successful.

7. Prefetching Data with SSR

Next.js supports Server-Side Rendering (SSR). You can prefetch data on the server and hydrate it on the client using TanStack Query.

Here’s an example of prefetching data in a Next.js page:

// app/page.js
import { dehydrate, HydrationBoundary, QueryClient } from "@tanstack/react-query";
import Posts from "../components/Posts";

async function fetchPosts() {
  const res = await fetch("https://jsonplaceholder.typicode.com/posts");
  if (!res.ok) {
    throw new Error("Failed to fetch posts");
  }
  return res.json();
}

export default async function Home() {
  const queryClient = new QueryClient();

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

  return (
    <HydrationBoundary state={dehydrate(queryClient)}>
      <Posts />
    </HydrationBoundary>
  );
}
Enter fullscreen mode Exit fullscreen mode

In the Posts component, you can use useQuery to access the prefetched data:

// components/Posts.js
"use client";

import { useQuery } from "@tanstack/react-query";

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

  return (
    <ul>
      {data.map((post) => (
        <li key={post.id}>{post.title}</li>
      ))}
    </ul>
  );
}
Enter fullscreen mode Exit fullscreen mode

8. React Query Devtools

The React Query Devtools are incredibly useful for debugging. They show the status of your queries, cache, and more.

To enable them, add the ReactQueryDevtools component to your providers.js file (as shown earlier). You can toggle the devtools with a button in the bottom-left corner of your app.


9. Common Patterns

Pagination

Use the useQuery hook with a dynamic query key to handle pagination:

const { data } = useQuery({
  queryKey: ["posts", page],
  queryFn: () => fetchPosts(page),
});
Enter fullscreen mode Exit fullscreen mode

Infinite Queries

For infinite loading, use the useInfiniteQuery hook:

const { data, fetchNextPage } = useInfiniteQuery({
  queryKey: ["posts"],
  queryFn: ({ pageParam = 1 }) => fetchPosts(pageParam),
  getNextPageParam: (lastPage, allPages) => lastPage.nextPage,
});
Enter fullscreen mode Exit fullscreen mode

10. Conclusion

TanStack Query is a game-changer for managing server state in Next.js applications. With its caching, background updates, and error handling, it simplifies data fetching and improves performance.

This guide covers the basics, but TanStack Query has many advanced features like mutations, optimistic updates, and more. Check out the official documentation for further learning.

Now that you’ve learned the fundamentals, it’s time to start building! Happy coding! 🚀


Further Reading:

Top comments (0)