The FontAwesome Icon featuring Next.js+TypeScript

Hacking the FontAwesome Library with Nextjs & TypeScript for Custom icon support

1. Overview

The aim of this article is to outline how to directly incorporate custom Fontawesome icons into your Next.js TypeScript project. For a solid introduction on setting up Fontawesome 5 with Next.js and TypeScript please see Vuong Dang’s recent post.

Vercel, Nextjs, GraphQL, and Heroku “fac” Icons via Hacked Font Awesome

2. A quick look under the hood

Create a lib directory in the root of your Next.js project. Then, create a fas-custom-integration.ts file where the magic will unfold. There is no tsx returned in the actual customization file itself which is why the library folder serves as its perfect home.

mkdir lib && cd lib && touch fas-custom-integration.ts && cd ..

Head to the @fortawesome package in node_modules. Then, open fontawesome-svg-core/index.d.ts to inspect the contents of the declaration file:

fontawesome-svg-core/index.d.ts

This is it, the librarians lair 📚. Examine the first two lines of this file. The very same Interfaces imported are exported immediately thereafter. What’s important to note when viewing declaration files like this is where Interfaces of potential utility such as IconDefinition and IconLookup reside. So let’s head on over to fontawesome-common-types/index.d.ts and view the contents of its declaration file.

3. Structure dictates function

Interfaces serve to describe the “shape” that values have in TypeScript. Personally, I prefer to think of this shape as its structure. If you’re familiar with biology you may recall that “structure dictates function” is known as the Central Dogma of Life. This thinking can be carried over to Interface usage in TypeScript; interface structure introduces strict type requirements which breathes life into the code we write. It’s do or die. Right, back to the hacking. With the contents of fontawesome-common-types/index.d.ts before us, the key to executing custom icon integration becomes increasingly clear.

fontawesome-common-types/index.d.ts

4. Dissecting Interfaces of utility

Since IconDefinition and IconLookup are of immediate utility for custom icon incorporation let’s break each down each individually.

4(a). IconLookup

This Interface describes two values, prefix and iconName. The prefix value is a string corresponding to "faX" as defined by the IconPrefix type above (where X=b, d, l, r, or s). The free version of Fontawesome supports "fab" and "fas" prefixes which denote brand and solid icon styles, respectively. Then there's iconName which is defined by the IconName type. This type describes an array of strings totaling out to 7,854 icon names in length.

IconLookup Interface in fontawesome-common-types/index.d.ts

4(b). IconDefinition

IconDefinition Interface in fontawesome-common-types/index.d.ts

Note that the IconDefinition Interface extends the IconLookup interface. This implies that the shape described by the IconLookup interface is extended, or copied, to the IconDefinition Interface. So, while it is evident that the shape of an icon is described by an array of five scalar values, it may not be as straightforward that the prefix and iconName values described by the IconLookup Interface will be prepended to the structure of our derived icon definition. IconDefinition extending IconLookup implies that our custom definition should look as follows:

Inferred custom icon structure

With that out of the way, let’s break down the five scalar values involved in defining the shape of an icon. Width and Height values derived from an SVG’s viewBox comprise the first two scalar values required when defining an icon. The viewBox of an SVG defines the position and dimension of a user viewport. There are always four numerical values in a viewBox which are invariably ordered as follows:

viewBox structure

viewBox structureThe third value involved in defining an icon corresponds to ligatures which are not important for our purposes. Read more about ligatures here. The fourth value of an icon denotes the “fill” of the SVG, or fillColor, as indicated by the presence of a hex-value. Lastly, the icon path(s) are ordered sequentially on a single line; there is a single space denoting row breaks within a path. If an SVG contains more than one path (d-value) a single space is used to denote the end of one path and the beginning of the next. This value is always confined to a single line of code (horizontal scrolling needs love too).

5. Translating theory to practice

With a plan of attack in mind, let’s get to it! Out of the node_modules and back to the fas-custom-integration.ts file in the lib directory we go. To prime the file for custom icon integration, import the library and the previously dissected Interfaces of utility:

Imports in ./lib/fas-custom-integration.ts

Since a real example requires a real SVG to derive values for our icon definition from, an SVG for the Vercel icon▲ is provided below (formerly ZEIT).

Vercel SVG

You might be asking yourself “Are you shamelessly promoting your favorite deployment platform?”

For those interested in utilizing a method that produces consistently formatted SVGs, save the SVG snippet above in a .svg file. Then, navigate to Figma, enter a workspace, and press Ctrl+Shift+k (or Cmnd+Shift+k for MacOS users out there). This opens your local filesystem allowing you to import the SVG as an image into the workspace. Then, right-click the newly added SVG image, hover over “copy”, and select “copy as SVG”. Paste the copied contents into your text editor and voila! The SVG from above is refactored as follows:

Figma mediated SVG (standardized)

With a standardized SVG obtained via Figma, let the excising begin. Your custom icon in ./lib/fas-custom-integration.ts should resemble the following:

Custom Icon in ./lib/fas-custom-integration.ts

Now, pass the faCustomVercelIcon into the library

Custom Icon passed to the library in ./lib/fas-custom-integration.ts

But wait, a TSLint error?! Not to worry, this is easily rectified. Navigate back to the @fortawesome package in node_modules and open fontawesome-common-types/index.d.ts once more. Simply add "vercel-icon" to the beginning of the IconName type:

Type IconName manipulation in fontawesome-common-types/index.d.ts

Looking good! The IconName type is now 7,855 icons strong. While we’re here, let’s tweak one additional type for the sake of clarity. At the very top of the index.d.ts file, add “fac” to the IconPrefix type:

Type IconPrefix manipulation in fontawesome-common-types/index.d.ts

The IconPrefix type denotes custom styled icons, or conquered, or whatever your heart desires. As long as you adhere to the faX notation for IconPrefix, Fontawesome is happy. With the TSLint error resolved, back to the fas-custom-integration.ts file in the lib directory we go. First, update the prefix to "fac". See? The library is more malleable than one might anticipate after all. There is one last modification to make but thankfully it does not involve returning to node_modules for a third time. Utilizing the extends property of TypeScript interfaces, the file should resemble the following:

./lib/fas-custom-integration.ts Comprehensive

While it is best practice to define all custom icons in one file, the CustomIconConstruct Interface that extends IconDefinition & IconLookup can now be imported throughout your project without having to go through the hastle of importing the two Interfaces of utility once more. So, how does this look in practice?

6. Importing to a tsx file to run locally

To test your custom icon out, open a file that ultimately renders to a pages directory file and import the following:

Imports in ./components/card-icons.tsx

The following snippet is from a project that’s currently under construction using Next, TypeScript, and Tailwindcss as frameworks.

Tsx snippet in ./components/card-icons.tsx

If you aren’t familiar with tailwindcss don’t worry about the className inline-styling above. That said, do note how the faCustomVercelIcon was received as props by the <FontAwesomeIcon /> JSX.Element. Success!

Custom Vercel Icon ▲ Success

7. Priming for production

In order for modifications made in node_modules to successfully deploy to a production environment, the following package(s) must be installed:
yarn
yarn add patch-package postinstall-postinstall
npm
npm i patch-package
Then, execute the following command:
npx patch-package @fortawesome/fontawesome-common-types

This generates a patches folder in your directory which houses a file outlining local updates made to the targeted package. In our case, it's the library in which we added "vercel-icon" to type IconName and "fac" to type IconPrefix. Then, add the following postinstall script in package.json:

adding a postinstall script in package.json

The postinstall script persists local changes made to node_modules even when packages are added, removed, or updated.
Update: postinstall-postinstall is only necessary to install for yarn. But why? While the postinstall script does run after `yarn` and `yarn add <package>`, it does not run after `yarn remove <package>`. Therefore, running `yarn remove <package>` in the absence of postinstall-postinstall renders the .patch file containing module modifications ineffective since the postinstall script isn’t executed (which I just learned firsthand). Fortunately, with postinstall-postinstall installed, the “postinstall” script is executed after running `yarn remove <package>`. However, the “postinstall” script will now be executed twice for `yarn` and `yarn add <package>` which implies that the “postinstall” script itself must be idempotent in nature (its value remaining unchanged when multiplied or otherwise operated on). End Update. The patch file generated after executing the aforementioned npx command should resemble the following:

generated file in ./patches/@fortawesome+fontawesome-common-types+0.2.30.patch
Custom SVG FontAwesome Icons…Stallions

In summary, this process can be performed with any SVG file. That said, I do recommend utilizing Figma (or a similar tool) to standardize SVGs before excising the essential bits required to define custom icons. This concludes my first ever tech post. Thanks for following along and feel free to drop any questions/comments/concerns in the comments below. Happy coding!

Originally published at https://dev.to on August 19, 2020.

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store