Over the past year I've had the pleasure of developing a full-stack application using the Remix framework and it was a bit tricky to get rid of the css jank when rendering Fontawesome icons.
When using Fontawesome via React, it dynamically injects css into pages that require icons. This is fine for client rendered applicatioins but for server-side rendered markup, the icon exists before the CSS is loaded leading to a visible icon resize (or in some cases, no resize leading to a weird-looking / broken layout).
Remix >= v1.9
The solution here is to add Fontawesome's css directly to the head
tag. Modify
root.tsx
as follows:
import { config as fontAwesomeConfig } from "@fortawesome/fontawesome-svg-core"; import fontAwesomeStyles from "@fortawesome/fontawesome-svg-core/styles.css"; fontAwesomeConfig.autoAddCss = false; export const links: LinksFunction = () => [ { rel: "stylesheet", href: globalStylesUrl, }, { rel: "stylesheet", href: fontAwesomeStyles, }, ];
Remix < v1.9
With Remix < 1.9, it was a bit tricky to inject arbitary css into the head
component. What was
supported was adding css via a link/href
. So the workaround was to create a resource route that
emits Fontawesome's css using its dom
object:
// api.font-awesome-css.tsx import { dom } from "@fortawesome/fontawesome-svg-core"; import { LoaderArgs } from "@remix-run/node"; export async function loader({ params }: LoaderArgs) { return stylesheet(dom.css()); } function stylesheet(content: string, init: ResponseInit = {}) { let responseInit = typeof init === "number" ? { status: init } : init; let headers = new Headers(responseInit.headers); if (!headers.has("Content-Type")) { headers.set("Content-Type", "text/css; charset=utf-8"); } return new Response(content, { ...responseInit, headers, }); }
We could then subequently modify root.tsx
to include this css and disable Fontawesome's automatic css
injection (since we are doing it manually):
// root.tsx import { config as fontAwesomeConfig } from "@fortawesome/fontawesome-svg-core"; fontAwesomeConfig.autoAddCss = false; export const links: LinksFunction = () => [ ...(cssBundleHref ? [ { rel: "stylesheet", href: globalStylesUrl, }, { rel: "stylesheet", href: cssBundleHref }, { rel: "stylesheet", href: "/api/font-awesome-css" }, ] : [ { rel: "stylesheet", href: globalStylesUrl, }, { rel: "stylesheet", href: "/api/font-awesome-css" }, ]), ];
Many thanks to Spencer for the original solution for Next.js and many thanks to Sébastien for pointing out the simpler version.