Solve HydrationError in Next.js with react-i18next Middleware
Solve HydrationError in Next.js with react-i18next Middleware

Fixing HydrationError in Next.js App Router with react-i18next and TypeScript

Fix Next.js HydrationError with react-i18next: Learn how to correctly setup SSR-friendly multilingual apps with middleware.7 min


Implementing multiple languages in your Next.js app can significantly enhance user experience, especially for international audiences. The popular library, react-i18next, provides a straightforward setup—until you run into the notorious HydrationError using Next.js app router and TypeScript.

Let’s start by looking at how you’d typically set up internationalization in your Next.js project using react-i18next, specifically having English and Finnish languages. A common practice is having a language switcher, allowing users to toggle between languages easily.

However, developers frequently encounter an issue when refreshing the page with Finnish (or any non-default language) selected. Upon page reload, Next.js may present a frustrating HydrationError, stating that server-rendered HTML doesn’t match the client-side content.

What Causes the HydrationError?

The Next.js HydrationError typically looks something like:

Error: Hydration failed because the initial UI does not match what was rendered on the server.

In simpler terms, Next.js (server-side) initially renders your app’s HTML based on the default or detected language. Later, when the JavaScript executes on the client side, react-i18next attempts to modify the content to the chosen language, causing a mismatch.

The reason for the mismatch? React expects precisely matching server-side and client-side markup. If there’s any variation—in our case, due to dynamic language changes—React throws this HydrationError, interrupting the app’s functionality.

Digging Into Your i18n.ts Configuration

Let’s analyze a typical i18n.ts setup you might use with react-i18next and Next.js. You’ve probably configured your file similarly to this:

import i18n from "i18next";
import { initReactI18next } from "react-i18next";
import LanguageDetector from "i18next-browser-languagedetector";
import HttpApi from "i18next-http-backend";

i18n.use(initReactI18next)
    .use(LanguageDetector)
    .use(HttpApi)
    .init({
        fallbackLng: "en",
        supportedLngs: ["en", "fi"],
        detection: {
            order: ["htmlTag", "cookie", "localStorage", "path", "subdomain"],
            caches: ["localStorage", "cookie"],
        },
        backend: {
            loadPath: "/locales/{{lng}}/{{ns}}.json",
        },
        react: {
            useSuspense: false,
        },
    });

export default i18n;

This configuration utilizes:

  • initReactI18next: Integrating i18next into React components seamlessly.
  • LanguageDetector: Automatically detecting the user’s preferred language from cookies, browser settings, or URL.
  • HttpApi: Loading translations dynamically via HTTP requests from your locales folder.

Your setup might also use cookies and localStorage to remember the user’s language preference—a smart, user-friendly decision.

The Issue with next-i18next and Next.js App Router

Previously, developers used a popular Next.js-specific library called next-i18next—a wrapper specially created for i18next usage in Next.js applications. However, as mentioned officially and discussed extensively on Stack Overflow, next-i18next currently doesn’t support the new Next.js app directory well.

Due to the structural and rendering changes introduced by the app router functionality in Next.js version 13 and above, next-i18next has trouble ensuring the server-side and client-side markup align properly. That’s the core reason behind these problematic HydrationErrors.

So, What Can You Do?

Since next-i18next isn’t ideal here, you’ll need an alternative strategy that fits seamlessly with Next.js app directory.

One common workaround involves carefully controlling when and where client-side language changes happen. By establishing identical server and client data, you can ensure React doesn’t stumble upon mismatched UI content during hydration.

Let’s explore that further and see how we can effectively tackle this issue.

Resolving the HydrationError Once and for All

To fix this, you’ll want to align your language detection strategy, eliminating mismatches between server-side rendering (SSR) and client-side rendering (CSR).

Here’s how you can approach it practically:

  1. Server-Side Language Detection: Instead of depending heavily on “browser-based” methods, focus on cookies and URL paths for detecting language on the server side. This way, when Next.js renders the initial HTML, your SSR language context is already predictable and safe from sudden client-side alterations.
  2. Reducing Client-side Language Modifications: Another strategy is using a custom middleware in Next.js to manage the language cookie proactively. Here’s an example of middleware placement (middleware.ts) in your app directory:
    import { NextResponse } from "next/server";
    import type { NextRequest } from "next/server";
    
    const supportedLanguages = ["en", "fi"];
    const defaultLanguage = "en";
    
    export function middleware(request: NextRequest) {
        const { pathname } = request.nextUrl;
        const langCookie = request.cookies.get("NEXT_LOCALE")?.value;
    
        if (langCookie && supportedLanguages.includes(langCookie)) {
            request.nextUrl.pathname = `/${langCookie}${pathname}`;
        } else {
            request.nextUrl.pathname = `/${defaultLanguage}${pathname}`;
        }
    
        return NextResponse.rewrite(request.nextUrl);
    }
    

    This ensures every request consistently provides Next.js with reliable language awareness directly from the cookie. It greatly reduces mismatches between server-side and client-side rendering.

  3. Implementing “use client” directive: When using client-side i18n components or hooks in the new app router, clearly declare "use client" directive in components to ensure Next.js knows precisely where client-side rendering occurs.
    "use client";
    import { useTranslation } from "react-i18next";
    
    export default function MyComponent() {
        const { t } = useTranslation();
        return <div>{t("hello")}</div>;
    }
    

    This explicitly informs Next.js the component operates on the client side.

Best Practices to Avoid Future Issues

Follow these practical tips to ensure smoother internationalization:

  • Unify detection methods: Limit language detection strategies to consistent, predictable sources like cookies or URL patterns.
  • Explicit client-side hydration: Avoid dynamic client-side language detection methods triggering re-renders after initial hydration.
  • Use Suspense wisely: Configuration with { useSuspense: false } like you did is smart as it helps to avoid accidental UI hiccups due to unexpected async behavior.
  • Preload Translations: Consider preloading your translation resources to ensure the initial render doesn’t suffer unnecessary delays or mismatches.

Handling HydrationErrors requires a nuanced understanding of the SSR and client-side environments of Next.js. Recognizing why these errors happen is half the battle—it’s mainly about aligning the expectations and outcomes of these rendering contexts.

In essence, address server-client mismatches proactively. By following the configuration and middleware strategies above, you’ll efficiently create a seamless multilingual experience without the annoyance of HydrationErrors.

Now your Next.js app with react-i18next is ready for smooth internationalization—no more confusing errors upon page refresh!

Have you encountered other issues or creative solutions regarding Next.js internationalization? Feel free to share your thoughts and experiences in the comments below!


Like it? Share with your friends!

Shivateja Keerthi
Hey there! I'm Shivateja Keerthi, a full-stack developer who loves diving deep into code, fixing tricky bugs, and figuring out why things break. I mainly work with JavaScript and Python, and I enjoy sharing everything I learn - especially about debugging, troubleshooting errors, and making development smoother. If you've ever struggled with weird bugs or just want to get better at coding, you're in the right place. Through my blog, I share tips, solutions, and insights to help you code smarter and debug faster. Let’s make coding less frustrating and more fun! My LinkedIn Follow Me on X

0 Comments

Your email address will not be published. Required fields are marked *