Site is under construction!

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 Fontsmakes 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

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 runbundle install && yarn install.

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

bin/rails generate controller welcome index
# config/routes.rb

Rails.application.routes.draw do
  get "welcome/index"
  root "welcome# index"
end

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 toGoogle Fonts. To keep it simple, I am going to useLato. 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 inapp/views/layouts/application.html.erbon the line above yourstylesheet_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"
/>

While we are here, let’s changestylesheet_link_tagtostylesheet_pack_tagand 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" %>
touch `
app/javascript/packs/application.scss
`

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

// app/javascript/packs/application.scss

html {
  font-family: 'Lato', sans-serif;
}

Now if we start the Rails server (bin/rails s), and navigate tolocalhost: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 thetypefacesproject from Gatsby founderKyle 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 fortypeface latowill reveal the package we are looking for, which we can easily install:

yarn add typeface-lato

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">

And the last step is requiring the package in ourapplication.jspack:

// app/javascript/packs/application.js
require('typeface-lato')

If we fire the Rails server back up and checkoutlocalhost:3000, the font should still be Lato! A quick look at thewebpacker-dev-serverlogs 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 ofjavascript_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!

This post is also available on DEV.

Want more Ruby & Rails content?

Subscribe to my mailing list! Don't worry, I won't be selling your data or spamming you with weekly emails. If you get an email from me, that means I put time into deciding if what I have to say at that time is worth stealing your attention for a few minutes.

    Support team

    © 2020 Andrew Mason. All rights reserved.

    Made with💙andBridgetownRB.