🖋Adding Fonts in Next.js (local fonts along with styled components)
Tags: nextjs, fonts, styledcomponents
Published: 2022-01-07 12:41:21

## Step 1 - Creating a Next.js application

I just like all my development folders to be on Desktop so i do cd Desktop

```jsx
cd Desktop
npx create-next-app
//Running this script on the folder you want to store the project folder
* Will ask you whats your project name? projectName
/*
Enter the project name and it will create a folder with that name along 
with all the neccessary files
*/
```

## Step 2 - Adding custom fonts (local)

📁 pages

📁 public

⠀⠀⠀📁 fonts

⠀⠀⠀⠀⠀⠀fontName-style.woff

## Step 3 - Preloading these fonts in _document.js.

Create _document.js file if you haven’t already and add the following code it preloads the font inside the Head.

If you are not using styled-component you should remove everything starting from static till render()

### _document.js

```jsx
import Document, {Html, Head, Main, NextScript } from 'next/document'
import { ServerStyleSheet } from 'styled-components'

export default class MyDocument extends Document {
//--------------For styled-components only------------//
  static async getInitialProps(ctx) {
    const sheet = new ServerStyleSheet()
    const originalRenderPage = ctx.renderPage

    try {
      ctx.renderPage = () =>
        originalRenderPage({
          enhanceApp: (App) => (props) =>
            sheet.collectStyles(<App {...props} />),
        })

      const initialProps = await Document.getInitialProps(ctx)
      return {
        ...initialProps,
        styles: (
          <>
            {initialProps.styles}
            {sheet.getStyleElement()}
          </>
        ),
      }
    } finally {
      sheet.seal()
    }
  }
//---------------------------------------------------//
  render() {
    return (
        <Html lang="en">
            <Head>
                <link
                  rel="preload"
                  href="/fonts/ProximaNova-Bold.woff"
                  as="font"
                  type="font/woff"
                  crossOrigin=""
                />
                <link
                  rel="preload"
                  href="/fonts/ProximaNova-Regular.woff"
                  as="font"
                  type="font/woff"
                  crossOrigin=""
                />
            </Head>
            <body>
                <Main />
                <NextScript />
            </body>
        </Html>
    );
}
}
```

- All that is important here is that <link> tag inside Head.
- PS: Html =/= html
    
    this 'Html" is common one for all the pages. 
    
    It is recommended that for using title and meta you do it inside a specific page's <Head/> tag.
    
- Also if you are not sure what all the code outside render means, i am using styled-component as my preferred choice for styling so that code is just for getting styled-components to work on Server side.  Check out [this blog](https://dev.to/dharmi/setting-up-styled-components-with-nextjs-952-3khf) to understand styled-components in Next.js for more details.

### Extra:

Short answer: Yes, You can use both. They serve different purpose and can be used in the same application.

According to nextjs website:

> Next.js uses the App component to initialize pages.To override, create the ./pages/_app.js file and override the App class
> 

and

> Pages in Next.js skip the definition of the surrounding document's markup. For example, you never include <html>, <body>, etc. To override that default behavior, you must create a file at ./pages/_document.js, where you can extend the Document class.
> 

**Note**: `_document.js` is only rendered on the server side and not on the client side. so event handlers like `onClick` is not going to work.

## Step 4 - Declaring them globally

(again this is using style-components. [Read more](https://dev.to/dharmi/setting-up-styled-components-with-nextjs-952-3khf)

### create a _app.js file in your pages folder if its not already and add the . following:

```jsx
import { createGlobalStyle } from "styled-components";

const GlobalStyle = createGlobalStyle`
    @font-face {
    font-family: 'ProximaNova-Regular';
    src: url('/fonts/ProximaNova-Regular.woff') format('woff');
    font-style: normal;
    font-weight: 400;
    font-display: swap;
  }
  @font-face {
    font-family: 'ProximaNova-Bold';
    src: url('/fonts/ProximaNova-Bold.woff') format('woff');
    font-style: bold;
    font-weight: 700;
    font-display: swap;
  }
`;

function MyApp({ Component, pageProps }) {
  return (
    <>
      <GlobalStyle />
      <Component {...pageProps} />
    </>
  );
}

export default MyApp
```

**Whats happening here?**

In my global styles i am declaring @font-face and the source of each face is pointing to the url of the font-faces inside the font folder we just created.

- Take care using @font-face, each font-family name is different and in accordance to the actual font file.
- font-display is kept to swap.

Now here if you are interested in knowing what font display actually does [Check out this article on CSS-tricks](https://css-tricks.com/almanac/properties/f/font-display/) also this is helpful in understanding FOUT and FINT which you can read more about [over here](https://leerob.io/blog/things-ive-learned-building-nextjs-apps).

## Thats it! Test it.

Now you add the following code inside index.js 

### index.js:

```jsx
import Head from 'next/head'
// import styles from '../styles/Home.module.css'
import styled from  'styled-components';
const Test1 = styled.p`
  font-size: 40px;
  font-family: 'ProximaNova-Regular';
  color: #00688B;
`;
const Test2 = styled.p`
  font-size: 40px;
  font-family: 'ProximaNova-Bold';
  color: #00688B;
`;

export default function Home() {
  return (
    <div>
      <Head>
        <title>Create Next App</title>
        <link rel="icon" href="/favicon.ico" />
      </Head>
      <main>
        <Test1>Test1</Test1>
        <Test2>Test2</Test2>
      </main>
    </div>
  )
```

Once this is added go to your terminal inside your folder and run:

```jsx
$ yarn dev
// $ just mean run it inside your bash terminal you just need to copy "yarn dev"
```

Once this is done go to [https://localhost.3000](https://localhost.3000) and see your next.js app running.

![Local Host testing](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jskwxsc9t44lbc3r2kw9.png)

## Helpful Debugging:

Check for Safari developer tools and Google Chrome developer tools:

If you see a fonts folder in there and inside them if you can see font file. That most probably means your fontfiles have been successfully be served and it is good to go.

Safari:
![Safari Debugging](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/njedwt35wsx9s5cccey7.png)

Google Chrome:
![Google Chrome Debugging](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ruwr2j5yaeky28o5tycs.png)

## Important Links

- Keep looking at this open source repository of Next.js actual site. [https://github.com/vercel/next-site](https://github.com/vercel/next-site)
    
    Specifically look at how they have declared fonts inside /styles/fonts.js and are calling it inside _app.js  
    
- If you observe closely there might be flashing of fonts happening in your project - Look more into FOUT (Flash of Unstyled Text) and FOIT (Flash of Invisible Text)  [https://leerob.io/blog/things-ive-learned-building-nextjs-apps](https://leerob.io/blog/things-ive-learned-building-nextjs-apps)
- I even found [this repository](https://github.com/adenekan41/codewonders) that uses the same libraries (next + styled components) you can have a look at it for any updates.

## Some mistakes i made:

Before trying anything else i used a library called next-fonts and they had created a next.config.js file. However at the time of speaking the library is deprecated and you can directly use fonts without any library. 

This file uses two libraries: 

1. next-fonts
2. @zeit/next-css

```jsx
const withCss = require("@zeit/next-css");
const withFonts = require("next-fonts");

module.exports = withFonts(
  withCss({
    webpack: (config, { isServer }) => {
      if (isServer) {
        const antStyles = /antd\/.*?\/style\/css.*?/;
        const origExternals = [...config.externals];
        config.externals = [
          (context, request, callback) => {
            if (request.match(antStyles)) return callback();
            if (typeof origExternals[0] === "function") {
              origExternals[0](context, request, callback);
            } else {
              callback();
            }
          },
          ...(typeof origExternals[0] === "function" ? [] : origExternals)
        ];

        config.module.rules.unshift({
          test: antStyles,
          use: "null-loader"
        });
      }
      return config;
    }
  })
);
```

or just install @zeit/next-css

```jsx
const withCSS = require('@zeit/next-css')

module.exports = withCSS({
  cssLoaderOptions: {
    url: false
  }
})
```

The point of this file is to stop css modules to render and prevent any conflicts. But i personally haven't used it for my own site.

### Links that helped me make this possible:

[https://stackoverflow.com/questions/60841540/flash-of-unstyled-text-fout-on-reload-using-next-js-and-styled-components](https://stackoverflow.com/questions/60841540/flash-of-unstyled-text-fout-on-reload-using-next-js-and-styled-components)

[https://codeconqueror.com/blog/using-google-fonts-with-next-js](https://codeconqueror.com/blog/using-google-fonts-with-next-js)

[https://leerob.io/blog/things-ive-learned-building-nextjs-apps](https://leerob.io/blog/things-ive-learned-building-nextjs-apps)

[https://github.com/styled-components/styled-components/issues/3224](https://github.com/styled-components/styled-components/issues/3224)

[How to avoid layout shifts caused by web fonts](https://simonhearne.com/2021/layout-shifts-webfonts)