Victor is a full stack software engineer who loves travelling and building things. Most recently created Ewolo, a cross-platform workout logger.
Server-side rendering Fontawesome icons in a Remix.run project

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.