Next.js Discord

Discord Forum

Hydration error with next-intl setup

Answered
Rojoss posted this in #help-forum
Open in Discord
I get the following error when I visit http://localhost:3000/nl
Hydration failed because the server rendered HTML didn't match the client. As a result this tree will be regenerated on the client. This can happen if a SSR-ed Client Component used
...
- lang="nl"

I have my RootLayout on src/app/layout.tsx
This one doesn't have a lang set on html tag
export default function RootLayout({ children }: Readonly<{ children: React.ReactNode }>) {
  return (
    <html>
      <body>{children}</body>
    </html>
  );
}


And then I have my AppLayout on src/app/[locale]/layout.tsx
Here I'm setting lang={locale} on html tag which comes from the params
import { routing } from "@/features/i18n/Routing";
import { NextIntlClientProvider } from "next-intl";
import { getMessages, setRequestLocale } from "next-intl/server";
import { notFound } from "next/navigation";

interface LayoutProps {
  children: React.ReactNode;
  params: { locale: string };
}

export default async function AppLayout({ children, params }: LayoutProps) {
  const { locale } = await params;
  if (!routing.locales.includes(locale)) {
    notFound();
  }
  setRequestLocale(locale);
  const messages = await getMessages();

  return (
    <html lang={locale}>
      <body>
        <NextIntlClientProvider messages={messages} locale={locale}>
          {children}
        </NextIntlClientProvider>
      </body>
    </html>
  );
}
Answered by joulev
so basically the issue is that, since the layouts are nested within each other, what you did ended up making it like this
<html>
  <body>
    <html lang="nl">
      <body>
        ...
      </body>
    </html>
  </body>
</html>

which is invalid html => hydration error.

if you need a custom 404 page, then keep the root layout but keep it empty
export default function RootLayout({ children }) {
  return children;
}

and add <html> and <body> tags to all pages not under the i18n layout (e.g., app/not-found.tsx)
View full answer

10 Replies

Oh there's no need for root layout? It stated in the docs to add it I believe
What about 404 pages and such they use root layout right?
@Rojoss Oh there's no need for root layout? It stated in the docs to add it I believe
so basically the issue is that, since the layouts are nested within each other, what you did ended up making it like this
<html>
  <body>
    <html lang="nl">
      <body>
        ...
      </body>
    </html>
  </body>
</html>

which is invalid html => hydration error.

if you need a custom 404 page, then keep the root layout but keep it empty
export default function RootLayout({ children }) {
  return children;
}

and add <html> and <body> tags to all pages not under the i18n layout (e.g., app/not-found.tsx)
Answer
Ah gotcha, that makes sense.
Thanks a lot!
Actually one more question.
Is there a way to get the locale now on the not-found.tsx page?
I have to put html tag in there now but how could I set the lang={locale} on there?
Do I have to make it a client component and just get the locale from browser?
@Rojoss Actually one more question. Is there a way to get the locale now on the `not-found.tsx` page? I have to put html tag in there now but how could I set the `lang={locale}` on there?
it depends on how you want to handle /[locale]/afhuwiehfuwia versus /hawuifehifhuwia, really.

but yeah a client component with useParams to get the locale would be my choice
Alrighty thanks