The Next.js team has successfully transitioned the App Router from beta to a stable release. The App Router uses Server Components. This makes working with Font Awesome icons even more "Font Awesome"!
Before we would have to destructure icons from the library like this:
// pages/example.js
import { faBug, faBugs } from '@fortawesome/free-solid-svg-icons'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
export default function MyBugsPage() {
return(
<>
<h1>Bugs 🐞</h1>
<FontAwesomeIcon icon={faBug} />
<FontAwesomeIcon icon={faBugs} />
</>)
}
If your application would have to many icons, you would could create a library file that you would import into _app.js
.
// _app.js
import "lib/icons";
...
// lib/icons.js
import { library } from "@fortawesome/fontawesome-svg-core";
import {
faBug,
faBugs,
} from "@fortawesome/pro-solid-svg-icons";
library.add(
faBug,
faBugs,
);
By doing this, you could skip the step on importing font awesome icons in the component.
// pages/example.js
export default function MyBugsPage() {
return(
<>
<h1>Bugs 🐞</h1>
<FontAwesomeIcon icon="bug" />
<FontAwesomeIcon icon="bugs" />
</>)
}
This is great, yet not ideal.
Centralizing the configuration of icons could substantially streamline the process. However, expanding from 2 to 50 (or more) icons may introduce several challenges:
- An extensive list may become difficult to sift through.
- It's annoying to add icons twice in the configuration file.
- Identifying unused icons can become complex.
- The JavaScript bundle delivered to the client is likely to increase in size.
To solve 1, 2, 3 we could do something like this:
// lib/icons.js
import { library } from "@fortawesome/fontawesome-svg-core";
import { fas } from "@fortawesome/pro-solid-svg-icons";
library.add(fas);
Unfortunately, point 4 becomes more of a problem. If you run next build
you will see that you are shipping a whooping 1mb+
to the client.
Route (pages) Size First Load JS
┌ ● / (564 ms) 9.66 kB 1.06 MB
├ /_app 0 B 1.05 MB
Yikes! 😕
Why is the bundle size hurting the performance?
This is due how rendering in Nextjs works. Scratch that! How it used to work. All components that are in the pages folder would be pre-render on the server and hydrated. While hydrating we would ship 1mb+
to the client. Nextjs uses tree shaking under hood, yet is not able understand how to do this with the fas
import.
The quickfix with App Router Server Components?
With the new App Router we can use Server Components without any set up required. All React (Server Components) run the code on the server and thus reduce bundle size that is sent to the client.
Before:
// pages/example.js 👈
export default function MyBugsPage() {
return(
<>
<h1>Bugs 🐞</h1>
<FontAwesomeIcon icon="bug" />
<FontAwesomeIcon icon="bugs" />
</>)
}
After:
// app/example/page.js 👈
export default function MyBugsPage() {
return(
<>
<h1>Bugs 🐞</h1>
<FontAwesomeIcon icon="bug" />
<FontAwesomeIcon icon="bugs" />
</>)
}
And you are done! 💫
Your bundle size should look, something like this:
Route (pages) Size First Load JS
┌ ● / (564 ms) 9.66 kB 84,5 kb
├ /_app 0 B 84,5 kb
NOTE: You need to have Nextjs version 13.4
.
If you starting fresh? Good for you, simply use the example above.
If you you are stuck with Nextjs older version, don't worry you can incrementally migrate by using this adoption guide, written by the Nextjs team.
Top comments (3)
No matter what I can't get icons defined in
lib/icons.js
to work. I'm importing this in top-level layout.tsx but then on page I need to importFontAwesomeIcon
each time. After that icon name is not recognized.What am I missing here?
Hi, great article, but your before and after example seems identical.
the comment is what's important: i.e., where you're putting the code.