javascriptreactjsnext.jsbrowser-history

Preventing creation of history entry when changing language in Next.js


I'm working on a Next.js project where we have a language dropdown for users to select their preferred language. The current implementation uses the router.push method from next/router to update the language and redirect to the current page without reloading. However, this creates a new entry in the browser history.

Here's the relevant code:

import { useRouter } from 'next/router';
import setLanguage from 'next-translate/setLanguage';


export const LanguageDropdown = () => {
  const { locales, locale } = useRouter();

  const updateLanguage = async (language: string) => {
    window.localStorage.setItem('language-selected', language.toLowerCase());
    // Assuming setLanguage is a function that updates the language in your app
    await setLanguage(language.toLowerCase());
  };

  return (
    <select onChange={(e) => updateLanguage(e.target.value)} value={locale}>
      {locales?.map((lang) => (
        <option key={lang} value={lang}>
          {lang.toUpperCase()}
        </option>
      ))}
    </select>
  );
};

The problem is that when a user changes the language, a new entry is created in the history. If the user then navigates back, they land on the previously set language and are then redirected to the new language. I would like to prevent this behavior and not create a new history entry when the language is changed.

Any suggestions on how to achieve this would be greatly appreciated.


Solution

  • Based on the current setLanguage function's implementation it doesn't appear this is possible as it uses router.push to PUSH a new entry onto the history stack.

    import Router from 'next/router'
    
    export default async function setLanguage(
      locale: string,
      scroll = true
    ): Promise<boolean> {
      return await Router.push(
        {
          pathname: Router.pathname,
          query: Router.query,
        },
        Router.asPath,
        { locale, scroll }
      )
    }
    

    But this hook is simple enough you could create your own that allows for PUSH or REPLACE navigation actions.

    Example:

    import Router from 'next/router';
    
    export default async function setLanguage(
      locale: string,
      { scroll = true, replace = false } = { scroll: true, replace: false },
    ): Promise<boolean> {
      return await Router[replace ? "replace" : "push"](
        {
          pathname: Router.pathname,
          query: Router.query,
        },
        Router.asPath,
        { locale, scroll }
      )
    }
    
    import { useRouter } from 'next/router';
    import setLanguage from '../path/to/setLanguage';
    
    
    export const LanguageDropdown = () => {
      const { locales, locale } = useRouter();
    
      const updateLanguage = async (language: string) => {
        const selectedLanguage = language.toLowerCase();
        window.localStorage.setItem('language-selected', selectedLanguage);
    
        // Set language and redirect
        await setLanguage(selectedLanguage, { replace: true });
      };
    
      return (
        <select onChange={(e) => updateLanguage(e.target.value)} value={locale}>
          {locales?.map((lang) => (
            <option key={lang} value={lang}>
              {lang.toUpperCase()}
            </option>
          ))}
        </select>
      );
    };