Importing Font Awesome into Next.js

Importing Font Awesome into Next.js

In today's article you are going to explore the simplest ways of importing Font Awesome into your Next.js application: JS-imports and CSS-imports. It is assumed that you are familiar with the Next.js React framework and the Font Awesome.

Note: We are going to dive deeply into details about how the things work, focusing on each step and why it is needed. If you are just looking for a quick solution, please navigate straight to either JS-imports method or CSS-imports final code.

Task overview

I use Font Awesome for many of my projects including my blog web app powered on Next.js. Hence the need of importing Font Awesome into the Next.js app :)

Importing Font Awesome into my app has not been trivial task for me, i.e. I couldn't do that right away (at least in the manner I wanted). So I browsed the web looking for a solution, but many of them seemed too complicated to me, others didn't work at all. After some trials I've found optimal solution for my project, and thus decided to share my experience with you in this article. So, let's get started.

Basically, what we want to accomplish in this post is to have that Twitter icon in the top navbar:

Task Overview

Here is how we'd write it in out code at it's origin (classic Font Awesome CSS within JSX).

<i className="fab fa-twitter"></i>

We're going to install Font Awesome into our app via npm:

$ npm install @fortawesome/fontawesome-free

Now, in order for it to work, we need to import Font Awesome into our code in some way. Let's start with the simplest one.

JS-imports: the simple way

I found this method on a StackOverflow discussion. All that you basically need to do is to import some Font Awesome JavaScript files into your Next.js custom _app.js:

// _app.js

import '@fortawesome/fontawesome-free/js/fontawesome';
import '@fortawesome/fontawesome-free/js/solid';
import '@fortawesome/fontawesome-free/js/regular';
import '@fortawesome/fontawesome-free/js/brands';
// ...

And it works like a charm, because everything is done by JavaScript itself. You can even customize what exactly you need to import, rather than importing everything.

I recommend this method for dynamic React applications, for example create-react-app based.

Caveats

However, I'm running a blog website, thus it is meant to be static. To show you what I'm talking about, I'll start my blog app in a dev mode (npm run dev), open my secondary web browser with JavaScript disabled and visit the localhost:3000:

SSR Failure

Can you see that? Nothing is rendered on the Twitter icon's place.

Now, you might ask me: "Anton, why to disable JavaScript at all? We're in 2020!" Well, indeed nowadays nobody has their JavaScript disabled, but by disabling it in your test browser you can watch how your app performs server side rendering.

And in my case, there is no server side rendering for my Twitter icon. There has to be another way out!

CSS-imports: the robust way

Alright, since Font Awesome is a CSS toolkit, is has to be possible to import their CSS files instead of JS files (as we can do with Bootstrap styles). Let's do that following official Font Awesome guidelines on importing via npm:

// _app.js

import '@fortawesome/fontawesome-free/css/all.css';
// ...

Now, if we visit our app on localhost:3000, we'll encounter an error like this one:

ModuleParseError: Module parse failed: Unexpected character '' (1:0)
You may need an appropriate loader to handle this file type, currently 
no loaders are configured to process this file...

Well, we didn't expect it to be so easy, did we? Still, what is going on here, what is this error about? We'll find the answer if we look at the Font Awesome core CSS-file node_modules/@fortawesome/fontawesome-free/css/all.css on line 4433 (for version 5.12.0, latest at the moment of publishing):

src: url("../webfonts/fa-regular-400.eot");

This is it: the Font Awesome CSS fails to import their web fonts.

Compiling font files

Next.js uses webpack for bundling under the hood, and it is not configured for compiling font files by default. We got to configure this on our own.

Webpack has an official file-loader for use cases like ours. Let's install it first:

$ npm install --save-dev file-loader

Now we'll configure our app to use this loader for font files:

// next.config.js

module.exports = {
  webpack: config => {
    config.module.rules.push(
      {
        test: /\.(woff(2)?|ttf|eot|svg)(\?v=\d+\.\d+\.\d+)?$/,
        use: [
          {
            loader: 'file-loader',
          },
        ],
      },
      // ...
    );
    return config;
  },
  // ...
};

If we now open our app in a browser, the error disappears, but:

Font Load Failure

Hmmm, okay... Obviously, a font we need has not been loaded. To figure out why, let's take a look at compiled CSS bundle (under .next/static/css directory, in my case the styles.chunk.css file) and find the corresponding line to the one we've seen in the core file (in my case line 4440):

src: url([object Module]);

What? What the hell is [object Module] and what is it doing in the CSS bundle?

Configuring webpack

The answer lies in the the file-loader esModule option. According to the docs, this option is set to true by default, therefore webpack tries to compile font files as EcmaScript modules. Let's fix that:

// next.config.js (excerpt)

// ...
{
  test: /\.(woff(2)?|ttf|eot|svg)(\?v=\d+\.\d+\.\d+)?$/,
  use: [
    {
      loader: 'file-loader',
      options: {
        esModule: false,
      },
    },
  ],
},
// ...

Beforehand I'll say that we will also need to set two more file-loader options: outputPath and publicPath. The outputPath tells the bundler where to place compiled font files. The publicPath is a path for web browser to request fonts from. Let's add the following lines into our config file:

// next.config.js (excerpt)

// ...
    {
      loader: 'file-loader',
      options: {
        esModule: false,
        outputPath: 'static/webfonts/',
        publicPath: '../webfonts/',
        // optional, just to prettify file names
        name: '[name].[ext]',
      },
    },
// ...

And this is what we've got in our app due to new configuration. The app build directory (fragment):

Build Directory

./next/static/css/styles.chunk.css, line 4440 (same as in previous sub-section):

src: url(../webfonts/fa-brands-400.eot);

If we now open our browser with disabled JavaScript, we'll see everything working:

Icon is rendering

Final code

Thus, in order to import Font Awesome into your Next.js app via CSS-imports, you will need to:

  1. Install Font Awesome (if you haven't yet):

    $ npm install @fortawesome/fontawesome-free
  2. Import Font Awesome CSS into your custom _app.js:

    // _app.js
    
    import '@fortawesome/fontawesome-free/css/all.css';
    // ...
  3. Install file-loader:

    $ npm install --save-dev file-loader
  4. Configure webpack in next.config.js for compiling font files:

    // next.config.js
    
    module.exports = {
      webpack: config => {
        config.module.rules.push(
          {
            test: /\.(woff(2)?|ttf|eot|svg)(\?v=\d+\.\d+\.\d+)?$/,
            use: [
              {
                loader: 'file-loader',
                outputPath: 'static/webfonts/',
                publicPath: '../webfonts/',
                // optional, just to prettify file names
                name: '[name].[ext]',
              },
            ],
          },
          // ...
        );
        return config;
      },
      // ...
    };

Conclusion

We've walked through two simple methods of importing Font Awesome into a Next.js app, however, the methods may be applied to other type of React apps as well. I hope it will facilitate for you this micro task of importing Font Awesome. Do you know any other ways to import Font Awesome into a React app? Tell about them in the Twitter thread of this article! Thanks for reading.

Photo by @ninjason from Unsplash