FontAwesome Subsetter
A Rails gem that tree-shakes FontAwesome 7 — scans your views and helpers for icon() calls, subsets WOFF2 font files to include only used glyphs, and generates minimal CSS.
Requirements
- FontAwesome files in
vendor/fontawesome/(metadata, scss, webfonts) - pyftsubset (from fonttools):
pip install fonttools brotli - sass-embedded gem for SCSS compilation
Installation
Add to your Gemfile:
gem "fontawesome_subsetter"
Then run bundle install.
Setup
Directory Structure
The gem expects FontAwesome files in your project at:
vendor/
fontawesome/
/
icons.yml
scss/
variables.scss
functions.scss
mixins.scss
core.scss
animated.scss
icons.scss
solid.scss
regular.scss
...
webfonts/
fa-solid-900.woff2
fa-regular-400.woff2
fa-brands-400.woff2
fa-duotone-900.woff2
fa-thin-100.woff2
Features
1. Icon Helper
The gem provides an icon helper available in all views:
= icon(:fas, :download, "Download", class: "mr-1")
= icon(:far, :check)
= icon(:fab, :github)
2. Puma Plugin (Development Watch Mode)
Add to your config/puma.rb:
if ENV.fetch("RAILS_ENV", "development") == "development"
plugin :fontawesome_subsetter
end
This watches app/views/, app/helpers/, and app/components/ for changes and automatically rebuilds subsetted fonts.
3. Rake Task (Deploy)
The gem automatically hooks into assets:precompile:
rake assets:precompile # fontawesome:subset runs first
You can also run it manually:
rake fontawesome:subset
Configuration
# config/initializers/fontawesome_subsetter.rb
FontawesomeSubsetter.configure do | config |
# Paths to scan for icon() calls (defaults shown)
config.scan_globs = ["app/views/**/*.slim", "app/helpers/**/*.rb"]
# Regex to match icon() calls (default shown)
config.icon_regex = /icon\(\s*:(?<prefix>fas|far|fab|fad|fal|fat)\s*,\s*:(?<icon>[\w_\-]+)\b/
# Override default paths (optional — defaults use Rails.root)
# config.meta_path = Rails.root.join("vendor", "fontawesome", "metadata", "icons.yml")
# config.fonts_dir = Rails.root.join("vendor", "fontawesome", "webfonts")
# config.build_dir = Rails.root.join("app", "assets", "builds")
# Custom SCSS template (optional — receives @styles hash, must return SCSS string)
# config.scss_template = ->(styles) { "..." }
# Additional SCSS variables to inject into the `@use "variables" with (...)` block.
# Keys may be symbols/strings (with or without leading `$`). Values are raw SCSS.
# config.variables = {
# "fa-font-display" => "swap",
# "fa-css-prefix" => '"fa"'
# }
# Optional presentational FontAwesome SCSS partials to include. Defaults to `:all`.
# Available: "sizing", "widths", "list", "bordered", "animated", "rotated-flipped", "stacked"
# (Core partials — functions, mixins, core, icons — are always included.)
# config.features = ["animated"]
end
Deploying with Kamal (Docker)
pyftsubset must be available during assets:precompile in your Docker build stage. Add the following to the build stage of your Dockerfile:
# In the build stage, install pyftsubset for font subsetting
RUN apt-get update -qq && \
apt-get install --no-install-recommends -y python3 python3-pip python3-venv pipx && \
pipx ensurepath && \
pipx install fonttools && \
pipx inject fonttools brotli && \
ln -s /root/.local/bin/pyftsubset /usr/local/bin/pyftsubset && \
rm -rf /var/lib/apt/lists /var/cache/apt/archives
Font subsetting runs automatically during assets:precompile, so no additional Dockerfile steps are needed — just make sure the line above appears before your RUN ... assets:precompile step.
The final (runtime) stage does not need pyftsubset; subsetted fonts are already baked into the image.
License
MIT — Copyright (c) 2026