Increase performance with Next.js and React Query

moiz ghumanmoiz ghuman
5 min read

In the world of web development, optimizing performance and user experience is crucial. One of the most effective ways to achieve this is through Static Site Generation (SSG) and hydration, especially when using frameworks like Next.js. This article delves deep into these concepts, explaining their processes, benefits, and how to implement them effectively.

What is Static Site Generation (SSG)?

Static Site Generation is a technique used to pre-render web pages at build time. This means that the HTML for each page is generated once during the build process, and served as static files. This approach contrasts with traditional server-side rendering, where HTML is generated on-the-fly for each request.

How Does SSG Work in Next.js?

Next.js leverages SSG by using the getStaticProps function to fetch data at build time and generate static HTML files. These files are then served to users, ensuring faster load times and improved performance.

Understanding Dehydration

Dehydration in the context of Next.js and React Query is the process of taking pre-fetched data and converting it into a JSON format. This JSON data is embedded in the HTML of the statically generated pages.

Why Use Dehydration?

Dehydration allows the data to be included directly in the HTML, which means that when a user visits the page, the data is already available. This avoids the need for additional server requests, speeding up the initial load time.

Embedding Data in HTML

After dehydrating the data into JSON format, it is embedded in the static HTML pages. This ensures that when the page loads, the data is readily available, allowing for a seamless user experience.

What is Hydration?

Hydration is the process that occurs during the initial page load. The client-side JavaScript takes the preloaded data (dehydrated JSON) embedded in the HTML and hydrates the React Query cache. This makes the data immediately available to the app, avoiding unnecessary server requests.

The Hydration Process

  1. Initial Page Load: The static HTML is served to the browser.

  2. Hydration: The client-side JavaScript (React) takes over and uses the embedded JSON data to populate the React Query cache.

  3. Data Availability: The app can now use the cached data as if it was fetched directly from the server, ensuring a fast and smooth user experience.

Benefits of SSG and Hydration

Improved Performance

By pre-generating HTML and embedding data in the page, the initial load time is significantly reduced. Users get a faster, more responsive experience.

SEO Advantages

Static pages are easier for search engines to crawl and index, improving the SEO performance of your website.

Reduced Server Load

Since the HTML is pre-generated and served as static files, the server load is reduced, allowing your application to handle more traffic.

Implementing SSG and Hydration in Next.js

Here’s a step-by-step guide to implementing SSG and hydration in a Next.js application using React Query.

Step 1: Setting Up Your Next.js Application

Start by creating a new Next.js project if you haven’t already.

npx create-next-app my-next-app
cd my-next-app

Step 2: Install React Query

Install React Query and the necessary dependencies.

npm install @tanstack/react-query

Step 3: Create the getStaticProps Function

Use getStaticProps to fetch data during build time.

pages/posts.jsx
import { dehydrate, QueryClient, useQuery } from '@tanstack/react-query'
import { getPosts } from '../lib/posts'

export async function getStaticProps() {
  const queryClient = new QueryClient()

  await queryClient.prefetchQuery(['posts'], getPosts)

  return {
    props: {
      dehydratedState: dehydrate(queryClient),
    },
  }
}

function Posts() {
  const { data } = useQuery(['posts'], getPosts)

  return (
    <div>
      <h1>Posts</h1>
      {data.map(post => (
        <div key={post.id}>{post.title}</div>
      ))}
    </div>
  )
}

export default Posts

Step 4: Hydrate the Data on the Client

Use the Hydrate component to populate the cache with the pre-fetched data.

pages/_app.tsx
import { Hydrate, QueryClient, QueryClientProvider } from "@tanstack/react-query"
import { ReactQueryDevtools } from "@tanstack/react-query-devtools"
import type { AppProps } from "next/app"
import { useState } from "react"

const MyApp = ({ Component, pageProps }: AppProps) => {
    const [queryClient] = useState(() => new QueryClient())

    return (
        <QueryClientProvider client={queryClient}>
            <Hydrate state={pageProps.dehydratedState}>
                <Component {...pageProps} />
            </Hydrate>
            <ReactQueryDevtools initialIsOpen={false} />
        </QueryClientProvider>
    )
}

export default MyApp

Example of Full Implementation

Let’s put it all together with a complete example:

lib/posts.js
export async function getPosts() {
  const res = await fetch('https://jsonplaceholder.typicode.com/posts')
  return res.json()
}

// pages/posts.jsx
import { dehydrate, QueryClient, useQuery } from '@tanstack/react-query'
import { getPosts } from '../lib/posts'

export async function getStaticProps() {
  const queryClient = new QueryClient()

  await queryClient.prefetchQuery(['posts'], getPosts)

  return {
    props: {
      dehydratedState: dehydrate(queryClient),
    },
  }
}

function Posts() {
  const { data } = useQuery(['posts'], getPosts)

  return (
    <div>
      <h1>Posts</h1>
      {data.map(post => (
        <div key={post.id}>{post.title}</div>
      ))}
    </div>
  )
}

export default Posts

// pages/_app.tsx
import { Hydrate, QueryClient, QueryClientProvider } from "@tanstack/react-query"
import { ReactQueryDevtools } from "@tanstack/react-query-devtools"
import type { AppProps } from "next/app"
import { useState } from "react"

const MyApp = ({ Component, pageProps }: AppProps) => {
    const [queryClient] = useState(() => new QueryClient())

    return (
        <QueryClientProvider client={queryClient}>
            <Hydrate state={pageProps.dehydratedState}>
                <Component {...pageProps} />
            </Hydrate>
            <ReactQueryDevtools initialIsOpen={false} />
        </QueryClientProvider>
    )
}

export default MyApp

Conclusion

Static Site Generation (SSG) and hydration are powerful techniques to optimize the performance and user experience of your Next.js applications. By pre-rendering pages at build time and using React Query for data management, you can ensure faster load times, improved SEO, and reduced server load.


FAQs

Q1: What is the main advantage of using Static Site Generation (SSG)? A1: The main advantage of SSG is improved performance, as the HTML for each page is pre-rendered and served as static files, ensuring faster load times.

Q2: How does dehydration improve page load speed? A2: Dehydration converts pre-fetched data into JSON and embeds it in the HTML, ensuring that the data is already available when the page loads, avoiding additional server requests.

Q3: What role does React Query play in SSG and hydration? A3: React Query manages data fetching and caching, allowing pre-fetched data to be embedded in the HTML (dehydration) and then used to populate the cache on the client-side (hydration).

Q4: Can I mix static site generation and client-side data fetching in Next.js? A4: Yes, you can mix SSG with client-side data fetching. Initial data can be pre-fetched during the build process, while additional data can be fetched on the client-side as needed.

Q5: How does SSG benefit SEO? A5: Static pages are easier for search engines to crawl and index, which can improve your website's SEO performance.

0
Subscribe to my newsletter

Read articles from moiz ghuman directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

moiz ghuman
moiz ghuman