2

One-sentence summary

When I have await serverSideTranslations inside getStaticProps, loading value from useSession() remains stuck as true.

Context

I use next-auth.js (v3) for auth. I have (gratefully) used this for a year without a problem.

const [session, loading] = useSession() is a hook to check if someone is signed in.

The loading option indicates the session state is being updated; it is true when session data is being fetched and false when it is done checking. https://stackoverflow.com/a/63191786/870121

I'm adding next-i18next to my project. Most of it's working, but I'm having some difficulty.

I've followed the instructions and example code for next-i18next.

It works well on pages with getServerSideProps: pages load, next-auth's useSession() still works, and translations work without warning or errors (example). For example, /login uses getServerSideProps like this:

export const getServerSideProps = async (context) => {
  const { locale } = context;
  console.log("Have locale, ", locale);
  return {
    props: {
      csrfToken: await csrfToken(context),
      ...(await serverSideTranslations(locale, ["common", "login"])),
    },
  };
};

and works well for /de/login and /en/login or just /login You might spy the word "loading…" briefly appear in the top-left of the nav bar (on desktop) on those two pages. This appears while loading is true, but then it quickly disappears. Spy "loading…" text appear in nav bar while 'loading' variable is true.

Problem

I'm running into some difficulty when I use it with getStaticProps. Specifically, loading returned by useSession() remains true.

Case A: When I have this code at page-level, translations work, but 'loading' value useSession() remains true

export const getStaticProps = async ({ locale }) => {
  const mood = "determined";
  return {
    props: {
      mood,
      ...(await serverSideTranslations(locale, ["common", "test-page"])),
    },
  };
};

Note the use of await before serverSideTranslations there; that's deliberate, as advised in all the docs I've seen.

The above causes this line to not work as expected; loading stays stuck on true… hence I don't know if the user is logged in.

import { useSession } from "next-auth/client";

const MyComponent = ()=>{
  const [session, loading] = useSession();
  return <div>loading is {loading ? "true" : "false"}</div>
}

Something about the await serverSideTranslations is causing useSession() to always return [undefined, true].

Note: I'm using next-auth v3, not v4.

Case B: I can remove the await keyword before serverSideTranslations, and then useSession() works as normal… but then translations don't work.

Any tips?

What I've tried so far:

I've focused on trying to adapt next-i18next to not need the await keyword. That took me down a rabbit hole of successive warnings/errors, each caused by the previous fix:

  • Remove "await" before serverSideTranslations in getStaticProps.
  • Try adding react: { useSuspense: false } to next-i18next.config.js per https://stackoverflow.com/a/69311205/870121 (didn't help).
  • Get error react-i18next:: You will need to pass in an i18next instance by using initReactI18next. → Try fixing by adding use: [initReactI18next], to next-i18next.config.js.
  • Get error, "appWithTranslation called without config" → fix by adding to _app.js
       import nextI18NextConfig from "../../next-i18next.config.js";
    
       // in page-level components:
       ...(await serverSideTranslations(
         locale,
         ["common", "login"],
         nextI18NextConfig
       )),
    
       // in pages/_app.js
       export default appWithTranslation(AwesoundApp, nextI18NextConfig);
    
    
  • Get errors like "Error: Error serializing ._nextI18Next.userConfig.use[0].init returned from getStaticProps;" → Add serializeConfig=false to next-i18next.config.js and pass config in to appWithTranslation as per "Unserialisable configs" instructions.
    None of the next-i18next docs recommend removing the await keyword though, so this feels like a losing battle / might introduce some unintended consequences

What I'd like help with:

Can I leave the await keyword before serverSideTranslations in the getStaticProps call, but still have loading not stuck on true? Can I leave my use of next-i18next as advised by those docs, but modify my nextauth hooks somehow so loading isn't stuck on true? Am I just missing some await keyword elsewhere in my code?

Example code

Compare these two pages:

To be clear: We want loading to quickly become false.

Those two pages have the following code.

next.config.js (extract):

const webpack = require("webpack");
const { i18n } = require("./next-i18next.config");

const moduleExports = {
  i18n, // see require import above
  webpack: (config, options) => {
    config.resolve.fallback = {
      perf_hooks: false, // as per "Quick Solution" on https://dev.to/marcinwosinek/how-to-add-resolve-fallback-to-webpack-5-in-nextjs-10-i6j
      fs: false, // 2022-02-02 hack to fix i18n errors, per https://github.com/isaachinman/next-i18next/issues/935#issuecomment-970024608
      path: false, // 2022-02-02 hack to fix i18n errors, per https://github.com/isaachinman/next-i18next/issues/935#issuecomment-970024608
    };
    return config;
  },
  …
}

next-i18next.config.js (in its entirety):

const path = require("path");

module.exports = {
  i18n: {
    defaultLocale: "en",
    locales: ["en", "de"],
    localePath: path.resolve("./public/locales"),
  },
  // react: { useSuspense: false },
  // useSuspense false does NOT prevent the warning,
  // "react-i18next:: You will need to pass in an i18next instance by using initReactI18next"  on pages that use getStaticProps and serverSideTranslations
  // per https://stackoverflow.com/a/69311205/870121
  // We only get this initReactI18next error when we do NOT use await before serverSideTranslations.
  debug: true,
};

pages/with-await.jsx (in its entirety):
See https://awesound-web-iow8f7d1o-awesound.vercel.app/de/test/with-await

// Similar to https://github.com/isaachinman/next-i18next/blob/ee5965183436d9b13d85c9187b3e09983b34ce7f/examples/simple/pages/second-page.js
// This has getStaticProps but not getStaticPaths
// Using 'await' with serverSideTranslations breaks next-auth useSession()

import Layout from "../../components/layout";
import { serverSideTranslations } from "next-i18next/serverSideTranslations";
import TranslationTestContent from "../../components/test/translationTest";

const usesAwait = true;

const TestPage = ({ mood }) => {
  return (
    <Layout>
      <TranslationTestContent mood={mood} usesAwait={usesAwait} />
    </Layout>
  );
};

export const getStaticProps = async ({ locale }) => {
  const mood = "determined";
  return {
    props: {
      mood,
      ...(await serverSideTranslations(locale, ["common", "test-page"])),
    },
  };
};

export default TestPage;

pages/no-await.jsx (in its entirety):
See https://awesound-web-iow8f7d1o-awesound.vercel.app/de/test/no-await
Note it's the same as pages/with-await.jsx but usesAwait is false

// Similar to https://github.com/isaachinman/next-i18next/blob/ee5965183436d9b13d85c9187b3e09983b34ce7f/examples/simple/pages/second-page.js
// This has getStaticProps but not getStaticPaths
// Using 'await' with serverSideTranslations breaks next-auth useSession()

import Layout from "../../components/layout";
import { serverSideTranslations } from "next-i18next/serverSideTranslations";
import TranslationTestContent from "../../components/test/translationTest";

const usesAwait = false;

const TestPage = ({ mood }) => {
  return (
    <Layout>
      <TranslationTestContent mood={mood} usesAwait={usesAwait} />
    </Layout>
  );
};

export const getStaticProps = async ({ params, locale }) => {
  const mood = "determined";
  return {
    props: {
      mood,
      ...serverSideTranslations(locale, ["common", "test-page"]),
    },
  };
};

export default TestPage;

Shared component,

import { useTranslation } from "next-i18next";
import Link from "next/link";
import { useSession } from "next-auth/client";
import { useRouter } from "next/router";
import ExclamationTriangleIcon from "../icons/exclamationTriangle";
import CheckCircleIcon from "../icons/checkCircle";

const TranslationTestContent = ({ mood, usesAwait = true }) => {
  const [session, loading] = useSession({ required: true });
  const { locale } = useRouter();
  const { t } = useTranslation("test-page");

  return (
    <main className="p-12">
      <div className="">Test static page with getStaticProps</div>
      <div className="">Do a full page refresh if you make any changes.</div>
      <h1>This page uses await: {usesAwait ? "yes" : "no"}</h1>
      <div>
        {usesAwait ? (
          <>
            Result: translations work, but the `loading` stays true; we don't
            know if user is logged in.
          </>
        ) : (
          <>
            Result: translations don't work, but the `loading` does become
            false.
          </>
        )}
      </div>
      <h3>Test translation</h3>
      <div className="p-4 bg-seco-200 m-4 flex space-x-6">
        <div>locale: {locale}</div>
        <div>language: {t("language")}</div>
        <div>
          {t("language") === "language" ? (
            <ExclamationTriangleIcon />
          ) : (
            <CheckCircleIcon />
          )}
        </div>
      </div>
      <Link href="/">
        <button type="button" className="btn btn-prim">
          {t("Test page button")}
        </button>
      </Link>
      <h3 className="mt-12">
        Test <pre className="inline">`loading`</pre>
      </h3>
      <div className="p-4 bg-seco-200 m-4">
        <div className="flex space-x-2 items-center">
          <pre className="inline">loading</pre>:{" "}
          <span className="inline">{loading ? "true" : "false"}</span>
          {loading ? <ExclamationTriangleIcon /> : <CheckCircleIcon />}
        </div>
        <div>
          <pre className="inline">session</pre> exists?{" "}
          <span className="inline">{session ? "true" : "false"}</span>
        </div>
      </div>
      <div className="border p-2 overflow-x-scroll">
        session: {JSON.stringify(session)}
      </div>
      <h3>Look at other props</h3>
      <div className="flex space-x-1 gap-x-1">
        Via <pre>props</pre>, we have <pre>mood</pre> = <pre>[{mood}]</pre>.
      </div>
    </main>
  );
};

export default TranslationTestContent;

Mark
  • 1,285
  • 1
  • 19
  • 28
  • might be this is an issue in useSession... did you create an issue on the github repo? https://github.com/isaachinman/next-i18next/issues but provide a reproducible example. – adrai Feb 04 '22 at 09:13
  • Thanks for your suggestion, Adrai. I started a new blank project with next-auth v4 and next-i18next here: https://github.com/markitics/next-auth-with-i18n. As noted in README, if you're signed in, then clicking on a route with `serverSideTranslations` causes user to appear logged out. If I refresh the page, I'm logged in again (no need to click "Sign in"). – Mark Feb 10 '22 at 14:14
  • Posted issue in github as recommended: https://github.com/isaachinman/next-i18next/issues/1680 I'm not sure if I need to change something with my `next-auth` integration, or with `next-i18next`. – Mark Feb 10 '22 at 14:42

0 Answers0