Making a subset of Font Awesome
Tags: javascript, grunt, automation, web
Published: 2022-05-22 20:57:53

---
title: Making a subset of Font Awesome
published: true
date: 2022-05-17 21:07:00 UTC
tags: javascript,grunt,automation,web
canonical_url: https://www.solarwinter.net/making-a-subset-of-font-awesome/
---

[Font Awesome](https://fontawesome.com/) is, well, awesome - but there's no getting away from the fact that if you only want a few of the icons then it's not a lightweight package.

Looking around for something to solve this with I found [Logan Graham](https://github.com/omacranger/) has written a nice JavaScript package to solve this, called - appropriately enough - [fontawesome-subset](https://github.com/omacranger/fontawesome-subset).

That will do the work of extracting the icons you want and repackaging them back into font files, but you still need the CSS files to use them.

It only works with Font Awesome 5.x - if you want to use 6 then it looks like you need to use the [Font Awesome Pro kits](https://fontawesome.com/plans), or [pull up the issue on Github](https://github.com/omacranger/fontawesome-subset/issues/16) and have a crack at it. :-)

After a couple of times trying to deploy this on a website and forgetting to copy or run _something_ I decided that it was probably time to invest in setting up a task runner.

## Which runner?

I’m sure there are more, but the ones I’ve seen are [Gulp](https://gulpjs.com) and [Grunt](https://gruntjs.com). I needed some way to decide which to go for, and the GitHub repos actually made that fairly simple.

### Gulp

Gulp hasn’t been updated since mid-2021, the CI test is failing, and it [doesn’t support Node >= 14](https://github.com/gulpjs/gulp/issues/2654).

### Grunt

Grunt was last updated 3 days ago (at the time of writing), and has passing CI tests.

So, Grunt it is.

## Demo webpage

Just to note this post isn’t going to walk you through actually using FA - that’s not the focus here. So if the classes below don’t make sense to you, head over to the FA docs.

Here’s the page:

```
<html>
  <head>
  </head>
  <body>
    <form>
      <label for="email">
        <i class="fas fa-envelope"></i>
        Email
      </label>
      <input type="email" id="email"/>
      <label for="password">
        <i class="fas fa-lock"></i>
        Password
      </label>
      <input type="password" id="password"/>
      <button type="submit">Click me</button>
    </form>
  </body>
</html>

```

Right now we don’t see any icons - we haven’t done the subsetting yet, and we don’t have any CSS files included.

![Email and Password field from a webpage, no icons visible.](https://www.solarwinter.net/content/images/2022/05/no-icons.png)
_Before we load any CSS, no icons to be seen_

We’ll use the `livereloadx` package for serving the content, since we don’t have to write any code to use it. We’ll add the command to the scripts section in `package.json`, to make it easy to run.

```
"scripts": {
    "serve": "node ./node_modules/.bin/livereloadx -s ."
}

```

## How to subset the files

After all, if we can't work this out there's not much point in doing the rest. Luckily, [the usage section of the README](https://github.com/omacranger/fontawesome-subset#usage) makes it fairly easy to work out.

Import the `fontawesomeSubset` function:

```
const { fontawesomeSubset } = require("fontawesome-subset");

```

Then we call it with two arguments. The first is a name, or array of names, for the icons that we want (without the `fa-` prefix). The second is where it should write the font files it's going to produce.

```
fontawesomeSubset([
  'envelope',
  'lock',
],
'static/webfonts');

```

And indeed, if we run that we can see it works, or at least produces some output. The output files are a fraction of the size of the source files, which is encouraging.

```
$ node fa_subset.js
$ fd . -t f static/
static/webfonts/fa-solid-900.eot
static/webfonts/fa-solid-900.svg
static/webfonts/fa-solid-900.ttf
static/webfonts/fa-solid-900.woff
static/webfonts/fa-solid-900.woff2
$ du -hs static/webfonts/
 20K	static/webfonts/

```

## Getting Grunt setup

First let's get Grunt to the point where it runs successfully, even if it doesn’t do anything.

Grunt config can live in various places, but I’m choosing to put it in `Gruntfile.js` to keep it separated. The simplest config is as below:

```
module.exports = function (grunt) {
  grunt.initConfig({
    pkg: grunt.file.readJSON("package.json")
  })

  // We'll load any plugins here.
  
  // 'default' is run if you run Grunt without arguments.
  grunt.registerTask('default', []);
}

```

Note the ‘default’ task being registered even though it doesn’t do anything. As well as being a good placeholder, it also means if you just run `grunt` with no arguments it won’t produce an error. We’ll come back to that later.

And with that, it's alive - though admittedly it doesn’t say much.

```
$ grunt

Done.

```

### Copying the files

To use the web fonts, we’ll use the CSS files from the Font Awesome package. They should automatically load the webfont files that we’ve extracted. Just a reminder - this only works with Font Awesome 5.x, not 6.x.

There’s a `grunt-contrib-copy` package which teaches Grunt how to copy files. The first thing after installing it is to load it in the Grunt setup function.

```
grunt.loadNpmTasks('grunt-contrib-copy');

```

From [the Grunt docs](https://gruntjs.com/configuring-tasks#building-the-files-object-dynamically) we can see that we need:

- the `expand` option, to enable the other options
- the `flatten` option, to flatten the destination results to a single level.
- `src`, to list the source files
- `dest`, to tell Grunt where to put the copied files.

From that we end up with a config object like this.

```
grunt.initConfig({
  pkg: grunt.file.readJSON("package.json"),
  copy: {
    main: {
      files: [
        {
          expand: true,
          flatten: true,
          src: [
            'node_modules/@fortawesome/fontawesome-free/css/fontawesome.min.css',
            'node_modules/@fortawesome/fontawesome-free/css/solid.min.css'
          ],
          dest: 'static/css/'
        },
      ]
    }
  }
});
```

We can run that, and it says it's copied two files. Let's just check it's the right two files, and they're where we expect.

```
$ grunt copy
Running "copy:main" (copy) task
Copied 2 files

Done.
$ fd . -t f static
static/css/fontawesome.min.css
static/css/solid.min.css

```

Yep, that looks good.

### Running the subset task

Now let's get Grunt to actually do the subsetting. For this we'll use the [registerTask()](https://gruntjs.com/creating-tasks#basic-tasks) function, which provides a general purpose “run this function” task. That has the following prototype:

```
grunt.registerTask(name, description, function)

```

So, let’s try just plugging in the function we wrote earlier.

```
grunt.registerTask('fasubset', 'Subset the FA icons.', function() {
  fontawesomeSubset([
      'envelope',
      'lock'
    ],
    'static/webfonts');
});

```

And … it works. That was almost too easy!

```
$ rm -rf static
$ grunt fasubset
Running "fasubset" task

Done.
$ fd . -t f static
static/webfonts/fa-solid-900.eot
static/webfonts/fa-solid-900.svg
static/webfonts/fa-solid-900.ttf
static/webfonts/fa-solid-900.woff
static/webfonts/fa-solid-900.woff2

```

### Running tasks by default

For the final bit of Grunt config, let's change the default task to run the copy and subset, so we can just run `grunt` in future without any arguments.

```
grunt.registerTask('default', ['fasubset', 'copy']);

```

## Add the CSS files to the webpage

Now we need to start using the CSS files, otherwise nothing will happen.

```
<html>
  <head>
    <link rel="stylesheet" href="/static/css/fontawesome.min.css">
    <link rel="stylesheet" href="/static/css/solid.min.css">
  </head>
[...]

```

## Check the results

Finally, let's check the webpage again - we should see that our icons have popped into existence.

![Email and Password field from a webpage, with envelope and padlock icons visible.](https://www.solarwinter.net/content/images/2022/05/with-icons.png)
_After loading CSS, with icons visible_

That's all there is to it! I have to admit I was expecting more difficulty when I started, but actually Grunt is designed nicely enough to make sense, and `fontawesome-subset` Just Works. I like packages like that.

How about you? Got any useful packages you'd like to share in the comments?

* * *