0

I am building a nextjs (v13) app routed website that uses next-intl for internationalization.

I am trying to figure out a way to have some context and state persist on locale change.
When locale is changed on the page, useRouter is triggered to direct to the correct locale route: app/[locale]/...rest. This seems to reset everything, including context that is not a child of NextIntlClientProvider. How can I prevent the total reset?

I am using Prefix-based routing ('example.com/en/...rest', or 'example.com/nl/...rest') which is default in next-intl.

------ My folder structure looks like this (following next-intl docs): ------

├── messages
│   ├── en.json
│   └── nl.json
├── middleware.ts
├── contexts
│   ├── testContext.tsx
└── app
    └── [locale]
        ├── layout.tsx
        └── page.tsx

------ '/middleware.ts' looks like this: ------

import createMiddleware from "next-intl/middleware";

export default createMiddleware({
  locales: ["en", "nl"],
  defaultLocale: "en",
});

export const config = {
  matcher: ["/((?!api|_next|.*\\..*).*)"],
};

------ '/contexts/testContext.tsx' looks like this: ------

"use client";

import {
  Dispatch,
  ReactNode,
  SetStateAction,
  createContext,
  useState,
} from "react";

type TestContext = {
  showDiv: boolean;
  setShowDiv: Dispatch<SetStateAction<boolean>>;
};

export const TestContext = createContext<TestContext | null>(null);

const TestContextProvider = ({ children }: { children: ReactNode }) => {
  const [showDiv, setShowDiv] = useState(false);

  return (
    <TestContext.Provider value={{ showDiv, setShowDiv }}>
      {children}
    </TestContext.Provider>
  );
};

export default TestContextProvider;

------ And the '/app/[locale]/layout.tsx' is as follows: ------

import { NextIntlClientProvider } from "next-intl";
import { notFound } from "next/navigation";
import TestContextProvider from "@/contexts/testContext";

export function generateStaticParams() {
  return [{ locale: "en" }, { locale: "nl" }];
}

export default async function RootLayout({
  children,
  params: { locale },
}: {
  children: React.ReactNode;
  params: { locale: string };
}) {
  let messages;
  try {
    messages = (await import(`../../messages/${locale}.json`)).default;
  } catch (error) {
    notFound();
  }

  return (
    <html lang={locale}>
      <TestContextProvider>
        <NextIntlClientProvider locale={locale} messages={messages}>
          <body>{children}</body>
        </NextIntlClientProvider>
      </TestContextProvider>
    </html>
  );
}

This is a testProject to specify this issue. In another- real- project this functionality would allow for (i.e.) my stateful dropdown menu (which includes a locale switch button) to remain in a state op being opened on locale change (which is being stored in a context outside of NextIntlClientProvider).

I have tried using the various UseRouter, UsePathname, Link and other hooks that are offered by next-intl as an extension of the native nextjs UseRouter, UsePathname and other hooks and functions, expecting this to take into consideration the specificities of locale switching (which ought to switch locale with the least amount of disruption.)

I have tried nesting the NextIntlClientProvider more deeply in the RootLayout, thereby only affecting more nested components. But this still triggered a total reset of external state and context.

I have tried relocating the RootLayout.tsx before the [locale] folder, thereby preventing the locale and route change to affect RootLayout (which holds my state and context). However, this would mean my Rootlayout can't use the internationalization as it is no longer embedded into the locale detection flow. Furthermore, if I would want to use internationalized metadata that uses translations, I would need to locate this within the [locale] detection and flow.

0 Answers0