DEV Community

Cover image for Instantly speed up your Rails application by self-hosting your fonts
Andrew Mason
Andrew Mason

Posted on • Updated on • Originally published at andrewm.codes

Instantly speed up your Rails application by self-hosting your fonts

Instantly speed up your Rails application by self-hosting your fonts

A font can make or break your design, and as a result many of us are probably not using the default system fonts. Google Fonts makes it really easy to find the perfect font, but it can come with a performance cost. If you are loading a font directly from Google, the following tutorial is guaranteed to speed up your Rails application.

Make your Rails app do this:

Cowboy Pooh Gif

(GET IT?!?)

To demonstrate, let's build a quick demo app.

This is a tutorial, but I will assume you have a basic understanding of Ruby on Rails. If not, and you need me to elaborate on anything, let me know in the comments.

Create a new Rails project

rails new self_hosted_webfonts_demo --skip-sprockets --skip-spring
cd self_hosted_webfonts_demo
Enter fullscreen mode Exit fullscreen mode

I am using Rails 6.0.2.2, which comes default with Webpacker 4.2.2, but I want to take advantage of features in v5, so I am going to update the gem and node package to v5.1.1. You are not required to do this in your application, but will need to if you are following along with this tutorial. Make sure you run bundle install && yarn install.

After we have upgraded webpacker, let's create a basic Welcome controller, and set the index as the root route in config/routes.rb:

bin/rails generate controller welcome index
Enter fullscreen mode Exit fullscreen mode
# config/routes.rb

Rails.application.routes.draw do
  get "welcome/index"
  root "welcome#index"
end
Enter fullscreen mode Exit fullscreen mode

Choose a font

Now that we have a landing page, we should snazz it up a bit with a nice font. If you're like me, this is usually when you head over to Google Fonts. To keep it simple, I am going to use Lato. Since I am not too sure all the styles and weights I need right now, I will just go ahead and select all the available styles (sound familiar?) and copy the link that Google provides.

Google Fonts

Now that we have our fonts, let’s add the link to the head of our application in app/views/layouts/application.html.erb on the line above your stylesheet_link_tag (line #7 if you are on a fresh Rails app):

<link href="https://fonts.googleapis.com/css2?family=Lato:ital,wght@0,100;0,300;0,400;0,700;0,900;1,100;1,300;1,400;1,700;1,900&display=swap" rel="stylesheet">
Enter fullscreen mode Exit fullscreen mode

While we are here, let's change stylesheet_link_tag to stylesheet_pack_tag and create our application styles file:

- <%= stylesheet_link_tag "application", media: "all", "data-turbolinks-track": "reload" %>
+ <%= stylesheet_pack_tag "application", media: "all", "data-turbolinks-track": "reload" %>
Enter fullscreen mode Exit fullscreen mode
touch `app/javascript/packs/application.scss`
Enter fullscreen mode Exit fullscreen mode

Inside of application.scss, add the following CSS rules to specify the font family:

// app/javascript/packs/application.scss

html {
  font-family: 'Lato', sans-serif;
}
Enter fullscreen mode Exit fullscreen mode

Now if we start the Rails server (bin/rails s), and navigate to localhost:3000, we should see our simple landing page being rendered with our nice, new font.

Welcome Page

The Problem...

Even though our view looks much better with the new font, we have just degraded the performance of our application and introduced a render blocking resource. When we load Lato, we are actually loading a stylesheet, and the browser will not render our page until it finishes retrieving the file from Google's servers.

Introducing a render blocking resource isn't great, but what's worse is we are now relying on Google to send us that file for us to render our page. Users will now be waiting longer for the page to load, and that time will fluctuate depending on how much traffic Google’s servers are handling.

Lighthouse Audit: CDN

Not great. Lighthouse isn't happy about it either.

However, there are solutions to this problem. I am going to show you the method I believe is the fastest to implement and easiest to understand, but understand there are several other fixes you could use instead with their own pros and cons.

The Solution

Enter the typefaces project from Gatsby founder Kyle Mathews. I highly recommend reading about his motivations behind the project, but the TL;DR is we can use Webpacker to install fonts on our server and self-host them ourselves instead of relying on Google.

Since I am already hosting everything else on my server, it makes perfect sense in a Rails environment to take this approach - and it's super quick to swap out Google Fonts for this solution.

A quick search on NPM for typeface lato will reveal the package we are looking for, which we can easily install:

yarn add typeface-lato
Enter fullscreen mode Exit fullscreen mode

Now let's remove the old way we were getting the font:

- <link href="https://fonts.googleapis.com/css2?family=Lato:ital,wght@0,100;0,300;0,400;0,700;0,900;1,100;1,300;1,400;1,700;1,900&display=swap" rel="stylesheet">
Enter fullscreen mode Exit fullscreen mode

And the last step is requiring the package in our application.js pack:

// app/javascript/packs/application.js
require("typeface-lato")
Enter fullscreen mode Exit fullscreen mode

If we fire the Rails server back up and checkout localhost:3000, the font should still be Lato! A quick look at the webpacker-dev-server logs will reveal that we are now self-hosting the same font styles and weights that we were before:

webpack-dev-server logs

Let's see if we have fixed the performance regression via Lighthouse:

Lighthouse Audit: Self Hosted

Lighthouse is no longer reporting a render blocking resource, we have boosted our performance!

Summary

We should be good to go! This is a simple, quick migration, which will reduce your time to first meaningful paint, and overall performance. It is also easily overlooked (speaking from personal experience).

It is worth noting that these Lighthouse audits were run against the Rails development server, and are not a true substitute for running them in production, but should give us a good enough idea of where we are at. For more accurate results, you should run these audits in production or start the application in production mode locally.

This change also positions you to make further enhancements, like requiring the fonts in a separate JavaScript pack, which will allow you to take advantage of javascript_packs_with_chunks_tag. I will leave that for you to explore, but you can see an example, along with the code for this tutorial, here.

Hopefully this was helpful! If you have taken another approach, I would be curious to hear about it in the comments.

Happy coding!

Top comments (3)

Collapse
 
tonydehnke profile image
Tony Dehnke

Neat.. I wonder if the same could be done for fonts in global.scss files..

ie in my project there is @import url("https://rsms.me/inter/inter.css");

Something to add to a trello card to do someday :)

Collapse
 
tonydehnke profile image
Tony Dehnke • Edited

Hmm, just looked for one of my fonts.. seems that Typeface is being depreciated. They recommend to use FontSource now

npmjs.com/package/typeface-inter

The Typefaces project is now deprecated.

@DecliningLotus created FontSource which provides the same functionality as Typefaces but with automated releases & richer support for importing specific weights, styles, or language subsets.

To start using Fontsource, replace in your package.json any instances of "typeface-inter" with "fontsource-inter".

Then change imports from "import 'typeface-inter'" to "import 'fontsource-inter/latin.css'".

Typeface packages will continue working indefinitely so no immediate changes are necessary.
Enter fullscreen mode Exit fullscreen mode
Collapse
 
davidteren profile image
David Teren

I was wondering how one could better include fonts via webpacker. Thanks for this.
PS: may I borrow from this for a post I'm busy writing. Will reference this post.