Abdullah Esmail

March 31, 2023

Custom fonts in production in Rails 7.0

I don't usually write blog posts. However, this made me want to share my experience and I'm sure someone will find it useful.

First things first. I love rails. When it comes to creating web apps, rails is always my #1 or #2 option; usually #1. I love how things just work. And I hate when things just don't. Like adding custom fonts.

It seems like certain things keep changing from version to version (looking at you asset pipeline) and you have to figure out how it should work either by experience or by trial and error.

I am working on a rails 7.0 app and needed to add custom fonts. I did a quick search online and found out that the "general advice" is to add a fonts/ folder under app/assets/ and put your fonts there. Well, it worked! Kind of, not really! This only worked in development. But it doesn't work in production even though the images under  the app/assets/images/ folder seem to work just fine. After doing:

rails assets:precompile

I can access all the images just fine. The public/assets/ folder has all the "processed" image files, but none of the font files under app/assets/fonts/... weird.

After digging deeper, I came across quite a few resources online suggesting that you have to add the path of the fonts folder to the Rails.application.config.assets.paths, like:

Rails.application.config.assets.paths << Rails.root.join('app', 'assets', 'fonts')

Well, that didn't work either. While inspecting Rails.application.config.assets.paths in the rails console, it does include the fonts folder by default. Quoting the Rails Guides (v7.0):

any path under assets/* will be searched.

So technically, you don't have to manually include the fonts/ folder if it is under app/assets/. So why doesn't rails include the fonts?

A few more attempts at it and I'm banging my head trying to figure out what I'm doing wrong. Most resources I found online were talking about earlier versions of rails.

After almost giving up, I took a close look at the app/assets/ folder to see why the files under the stylesheets/ and images/ folders were picked up by rails but not anything under the fonts/ folder. I noticed a config/ folder there which included one file only; a manifest.js file.

I opened the app/assets/config/manifest.js file and it has the following default content:

//= link_tree ../images
//= link_directory ../stylesheets .css
//= link_tree ../../javascript .js
//= link_tree ../../../vendor/javascript .js

Is this why images and stylesheets can be linked? Does it have to be told about the fonts/ folder? Let's try it! I added a line to the beginning of the file:

//= link_tree ../fonts
//= link_tree ../images
//= link_directory ../stylesheets .css
//= link_tree ../../javascript .js
//= link_tree ../../../vendor/javascript .js

Then did:

rails assets:precompile

Lo and behold! Font files exist in public/assets/ along with image and stylesheet files! Let's check our app in the browser. Nope! All requests to font files still return HTTP 404 responses.

Checking the actual font files under public/assets/, since they're precompiled, font file names are fingerprinted. This is why if you have a font file named, let's say, app/assets/fonts/my-awesome-font.ttf, after precompiling, it will become something like:

public/assets/my-awesome-font-e31e38b25f8d606b4946d18ea.ttf

which can be accessed obviously like:

https://example.com/assets/my-awesome-font-e31e38b25f8d606b4946d18ea.ttf

which works fine. But, HOW do I know the value of the random characters (the digest) at the end of the filename?

Well, thankfully, you don't have to. Rails provides helpers for doing just that. Instead of linking your font files like:

@font-face {
  font-family: "My Awesome Font";
  font-style: normal;
  font-weight: 400;
  font-display: block;
  src: url("/assets/my-awesome-font-e31e38b25f8d606b4946d18ea.ttf");
}

You should do the following:

@font-face {
  font-family: "My Awesome Font";
  font-style: normal;
  font-weight: 400;
  font-display: block;
  src: font-url("my-awesome-font.ttf");
}

One thing you have to change before this takes effect. Change the .css file extension to .scss.

Voila! Fonts are picked up and are displayed nicely on the website.


Summary

  1. Create an app/assets/fonts/ folder.
  2. Put your fonts under that folder.
  3. Link the fonts/ folder in app/assets/config/manifest.js file:
    1. Add //= link_tree ../fonts to the file.
  4. Use helpers to link to the fonts: font-url("font-name.ttf");
  5. Change .css to .scss
  6. Run rails assets:precompile


I really hope this helps anyone out there looking for a more recent example of how to add custom fonts. Also, since this is my first post ever, I enjoyed writing it and I think I might start writing more posts.

About Abdullah Esmail

I'm a rails developer and loving it. I love simplifying things. The author of Kaffy. When I'm not coding, I'm drinking coffee and checking what other people are coding. I also enjoy working with elixir and flutter. Still have high hopes for RubyMotion.