Skip to main content

Using FontAwesome in Pheonix

Easily integrate Font Awesome icons into your Phoenix web app.

· By Joe Bellus · 3 min read

Phoenix ships with HeroIcons, which is great for out-of-the-box free icons. FontAwesome also has a fantastic set of free icons, as well as a stellar set of paid ones. It's easy to configure Phoenix/Tailwind to use FontAwesome instead of HeroIcons, or use them both together.

FontAwesome as a Dependency

As a starting point, lets assume a brand new Phoenix application generated with mix phx.new.

Add the font awesome package as an NPM dependency:

cd assets
npm init -y
npm install --save @fortawesome/fontawesome-free

Configuring Tailwind

With FontAwesome installed and ready, we need to make changes to the tailwind.config.json. There is an array that should contain several plugin declarations. We can add this one just below the Hero Icon declaration, or replace it if they are no longer needed.

plugin(function ({ matchComponents, theme }) {
  let iconsDir = path.join(
    __dirname,
    "node_modules/@fortawesome/fontawesome-pro/svgs",
  );
  let values = {};
  let icons = [
    ["", "/regular"],
    ["-duotone", "/duotone"],
    ["-light", "/light"],
    ["-solid", "/solid"],
    ["-thin", "/thin"],
    ["-brands", "/brands"],
  ];
  icons.forEach(([suffix, dir]) => {
    fs.readdirSync(path.join(iconsDir, dir)).forEach((file) => {
      let name = path.basename(file, ".svg") + suffix;
      values[name] = { name, fullPath: path.join(iconsDir, dir, file) };
    });
  });
  matchComponents(
    {
      fa: ({ name, fullPath }) => {
        let content = fs
          .readFileSync(fullPath)
          .toString()
          .replace(/\r?\n|\r/g, "");
        let size = theme("spacing.6");
        if (name.endsWith("-mini")) {
          size = theme("spacing.5");
        } else if (name.endsWith("-micro")) {
          size = theme("spacing.4");
        }
        return {
          [`--fa-${name}`]: `url('data:image/svg+xml;utf8,${content}')`,
          "-webkit-mask": `var(--fa-${name})`,
          mask: `var(--fa-${name})`,
          "mask-repeat": "no-repeat",
          "background-color": "currentColor",
          "vertical-align": "middle",
          display: "inline-block",
          width: size,
          height: size,
          "mask-size": "contain",
          "mask-position": "center",
        };
      },
    },
    { values },
  );
})

assets/tailwind.config.json

Modifying the Icon Component

Inside core_components.ex, there is an icon/1 function that provides the icon component for views. By default, it supports Hero Icons. We can modify this to also support FontAwesome icons. We will also add a spin attribute to the component to enable FA's spin animation.

  attr :name, :string, required: true
  attr :class, :string, default: nil
  attr :spin, :boolean, default: false

  def icon(%{name: "hero-" <> _} = assigns) do
    ~H"""
    <span class={[@name, @class]} />
    """
  end

  def icon(%{name: "fa-" <> _} = assigns) do
    ~H"""
    <span class={[@name, @spin && ["animate-spin"], @class]} />
    """
  end

lib/project_name/components/core_components.ex

If you don't need Hero Icons at all, you can remove the first function declaration entirely.

Trying It Out

We will add a spinning user icon to the top of the home page as a test. Add the following code to the top of home.html.heex.

<.icon name="fa-user" spin />

lib/project_name/controllers/page_html/home.html.heex

Now load up the site with mix phx.server, browse to http://localhost:4000, and the icon should be visible at the top of the page.

Using FontAwesome Pro

If you have a FontAwesome Pro subscription, the steps are similar.

Create a .npmrc File

You need a .npmrc file in your assets path with the authorization token for your FontAwesome access.

@fortawesome:registry=https://npm.fontawesome.com/
//npm.fontawesome.com/:_authToken=<<YourTokenHere>>

assets/.npmrc

Font Awesome Dependency

Instead of installing the free font awesome library, install the pro version:

cd assets
npm init -y
npm install --save @fortawesome/fontawesome-pro

Configuring Tailwind

The tailwind configuration is similar, but with more styles and different paths:

    plugin(function ({ matchComponents, theme }) {
      let iconsDir = path.join(
        __dirname,
        "node_modules/@fortawesome/fontawesome-pro/svgs",
      );
      let values = {};
      let icons = [
        ["", "/regular"],
        ["-duotone", "/duotone"],
        ["-light", "/light"],
        ["-solid", "/solid"],
        ["-thin", "/thin"],
        ["-brands", "/brands"],
      ];
      icons.forEach(([suffix, dir]) => {
        fs.readdirSync(path.join(iconsDir, dir)).forEach((file) => {
          let name = path.basename(file, ".svg") + suffix;
          values[name] = { name, fullPath: path.join(iconsDir, dir, file) };
        });
      });
      matchComponents(
        {
          fa: ({ name, fullPath }) => {
            let content = fs
              .readFileSync(fullPath)
              .toString()
              .replace(/\r?\n|\r/g, "");
            let size = theme("spacing.6");
            if (name.endsWith("-mini")) {
              size = theme("spacing.5");
            } else if (name.endsWith("-micro")) {
              size = theme("spacing.4");
            }
            return {
              [`--fa-${name}`]: `url('data:image/svg+xml;utf8,${content}')`,
              "-webkit-mask": `var(--fa-${name})`,
              mask: `var(--fa-${name})`,
              "mask-repeat": "no-repeat",
              "background-color": "currentColor",
              "vertical-align": "middle",
              display: "inline-block",
              width: size,
              height: size,
              "mask-size": "contain",
              "mask-position": "center",
            };
          },
        },
        { values },
      );
    })

assets/tailwind.config.json

The sourcecode for this tutorial is available on GitHub

View Source
Updated on Jun 5, 2025