Search code examples
reactjstypescriptnext.jsfonts

Font loaders must be called and assigned to a const in the module scope on Nextjs13


Am trying to load google font to Nextjs project but I'm getting the error below. In nextjs12 It works okay but I have to use the link for the fonts in the head. This error occurs also using @next/font/local.

I've struggled with this and any help is appreciated.

Am using the official docs process for adding global fonts. Am on Nextjs13.

My _app.tsx code is:

import { useApollo } from "@api/apollo/apolloclient";
import { ApolloProvider } from "@apollo/client";
import { CacheProvider, EmotionCache } from "@emotion/react";
import { Ubuntu } from "@next/font/google";

import "focus-visible/dist/focus-visible";

import { DefaultSeo } from "next-seo";
import { AppProps } from "next/app";

import { ChakraProvider, ColorModeScript } from "@chakra-ui/react";

import theme from "@definitions/chakra/theme";
import { ThemeColorProvider } from "@definitions/context/theme";
import createEmotionCache from "@definitions/utils/createEmotionCache";

import Layout from "@layouts/default";

import "@styles/app.css";
import "@styles/global.scss";

import SEO from "../../next-seo.config";
import { useEffect, useState } from "react";

type ComponentWithPageLayout = AppProps & {
  Component: AppProps["Component"] & {
    PageLayout?: React.ComponentType;
  };
  emotionCache: EmotionCache;
};

const clientSideEmotionCache = createEmotionCache();

function sApp({
  Component,
  emotionCache = clientSideEmotionCache,
  pageProps,
}: ComponentWithPageLayout): JSX.Element {
  const apolloClient = useApollo(pageProps);
  const AnyComponent = Component as any;
  const Layoutio = Component.PageLayout as any;

  const ubt = Ubuntu({
    weight: ["300", "400", "500", "700"],
    style: ["normal", "italic"],
  });

  const [showChild, setShowChild] = useState(false);
  useEffect(() => {
    setShowChild(true);
  }, []);

  if (!showChild) {
    return null;
  }

  if (typeof window === "undefined") {
    return <></>;
  } else {
    return (
      <>

      <style jsx global>{`
          html {
            font-family: ${ubt.style.fontFamily};
          }
        `}</style>

        <CacheProvider value={emotionCache}>
          <ApolloProvider client={apolloClient}>
            <ThemeColorProvider>
              <ChakraProvider theme={theme}>
                <ColorModeScript
                  initialColorMode={theme.config.initialColorMode}
                />
                <DefaultSeo {...SEO} /> 
                {Component.PageLayout ? (
                  <Layoutio>
                    <AnyComponent {...pageProps} />
                  </Layoutio>
                ) : (
                  <Layout>
                    <AnyComponent {...pageProps} />
                  </Layout>
                )}
              </ChakraProvider>
            </ThemeColorProvider>
          </ApolloProvider>
        </CacheProvider>
      </>
    );
  }
}

export default App;

enter image description here


Solution

  • Under _app.tsx the minimal code that worked is shown below. Am using chakra. The downside is that this is experimental and worked on some devices and failed on some like Safari.

    import { useApollo } from "@api/apollo/apolloclient";
    import { EmotionCache, CacheProvider } from "@emotion/react";
    import { Ubuntu } from "@next/font/google";
    
    import "focus-visible/dist/focus-visible";
    
    import { DefaultSeo } from "next-seo";
    import { AppProps } from "next/app";
    
    import theme from "@definitions/chakra/theme";
    import { ThemeColorProvider } from "@definitions/context/theme";
    import createEmotionCache from "@definitions/utils/createEmotionCache";
    
    import "@styles/app.css";
    import "@styles/global.scss";
    
    import SEO from "../../next-seo.config";
    import { ChakraProvider, ColorModeScript } from "@chakra-ui/react";
    import { ApolloProvider } from "@apollo/client";
    
    type ComponentWithPageLayout = AppProps & {
      Component: AppProps["Component"] & {
        PageLayout?: React.ComponentType;
      };
      emotionCache: EmotionCache;
    };
    
    const clientSideEmotionCache = createEmotionCache();
    
    function sApp({
      Component,
      emotionCache = clientSideEmotionCache,
      pageProps,
    }: ComponentWithPageLayout): JSX.Element {
      const apolloClient = useApollo(pageProps);
      const AnyComponent = Component as any;
      const Layoutio = Component.PageLayout as any;
    
      const ubt = Ubuntu({
        weight: ["300", "400", "500", "700"],
        style: ["normal", "italic"],
      });
    
      return (
        <>
          <CacheProvider value={emotionCache}>
            <ApolloProvider client={apolloClient}>
              <ThemeColorProvider>
                <ChakraProvider theme={theme}>
                  <ColorModeScript
                    initialColorMode={theme.config.initialColorMode}
                  />
                  <DefaultSeo {...SEO} />
                   //Wrap component with classname
                  <main className={ubt.className}>
                    <AnyComponent {...pageProps} />
                  </main>
                </ChakraProvider>
              </ThemeColorProvider>
            </ApolloProvider>
          </CacheProvider>
        </>
      );
    }
    
    export default App;
    

    For next config add this block in nextconfig.

      experimental: {
    appDir: true,
    fontLoaders: [ 
      { loader: "@next/font/google", options: { subsets: ["latin"] } },
    ],},