Skip to content

feat(gatsby-plugin-preload-fonts): preload used fonts - #14608

Merged
wardpeet merged 22 commits into
gatsbyjs:masterfrom
superhawk610:plugin/preload-fonts
Aug 23, 2019
Merged

feat(gatsby-plugin-preload-fonts): preload used fonts#14608
wardpeet merged 22 commits into
gatsbyjs:masterfrom
superhawk610:plugin/preload-fonts

Conversation

@superhawk610

@superhawk610 superhawk610 commented Jun 7, 2019

Copy link
Copy Markdown
Contributor

Description

Fonts don't start downloading until the browser knows they're needed. Which means that the browser has to download/parse the HTML & CSS and start rendering a section of the page before it knows to start downloading the font.

This can cause significant delays in loading fonts.

A common suggestion is to add a to the page for each font that's used on the page so the browser knows to start downloading the font files immediately.

This plugin enables a two-step process for embedding <link preload> tags into each statically rendered page in your build output:

  1. Run the included puppeteer script to scrape every route in your application and generate a mapping from routes to fonts.
  2. Add the plugin to your gatsby-config.js and these tags will be automagically embedded into each page at build time.

TODO

  • list all application routes
  • determine what fonts are required on each route
  • cache resource mapping between builds to prevent unnecessarily scrapes
  • inject tags at build time
  • configuration
  • docs
  • tests

Related Issues

Closes #14281.

@superhawk610 superhawk610 self-assigned this Jun 7, 2019
@superhawk610
superhawk610 marked this pull request as ready for review June 8, 2019 04:39
@superhawk610
superhawk610 requested review from a team, KyleAMathews and papandreou June 8, 2019 04:39
@superhawk610

Copy link
Copy Markdown
Contributor Author

I'm currently working on unit tests but I figured I could go ahead and get some feedback on the plugin layout/implementation - in particular, I'm curious how my approach to the following tasks fit the Gatsby ecosystem:

  • cache directory
  • logging
  • route fetching

@ghost

ghost commented Jun 13, 2019

Copy link
Copy Markdown

Subscribed to this! Thanks for taking this on @superhawk610

@wardpeet wardpeet self-assigned this Jun 14, 2019

@wardpeet wardpeet left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for taking this one on! Looks pretty good.

I wonder if we can implement this as a real gatsby plugin instead of a seperate script. This gives us the opportunity to use the gatsby-cli reporter & gatsby cache.

We could look into https://www.gatsbyjs.org/docs/node-apis/#onCreateDevServer to run puppeteer when the dev server is running.

What do you think?

Comment thread packages/gatsby-plugin-preload-fonts/README.md Outdated
Comment thread packages/gatsby-plugin-preload-fonts/src/prepare/utils.js Outdated
Comment thread packages/gatsby-plugin-preload-fonts/src/prepare/index.js Outdated
@superhawk610

Copy link
Copy Markdown
Contributor Author

@wardpeet I went with a separate script to follow the idea proposed in the original linked issue; I'm cool with either integrating with the dev server directly or having that as an option, my only concern is that this is a costly operation (minutes on smaller projects and potentially hours on larger projects). I added some basic route caching to prevent crawling if the list of routes hasn't changed, perhaps I should make that more granular and only crawl new routes by default?

cc @KyleAMathews: what are your thoughts on having the font-scraping script run onCreateDevServer vs manually via a package script?

@KyleAMathews

Copy link
Copy Markdown
Contributor

Yeah — my guess that this would be a really costly operation so best done in a periodic cron job — it looks like your testing has confirmed that so let's keep it a separate script.

@KyleAMathews KyleAMathews left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good!

Could you include a screenshot of what a run of the script looks like?

Comment thread packages/gatsby-plugin-preload-fonts/README.md Outdated
@superhawk610

Copy link
Copy Markdown
Contributor Author

Here's a couple screen recordings of the preload script:

Initial Run

first-run

Subsequent Run(s)

subsequent-run

@wardpeet wardpeet left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks really good! I added a few comments/questions but this is ready to go! Well done! 👏

Comment thread packages/gatsby-plugin-preload-fonts/src/prepare/cache.js Outdated
Comment thread packages/gatsby-plugin-preload-fonts/src/prepare/fetch-routes.js Outdated
Comment thread packages/gatsby-plugin-preload-fonts/src/prepare/index.js Outdated
Comment thread packages/gatsby-plugin-preload-fonts/src/prepare/logger.js Outdated
Comment thread packages/gatsby-plugin-preload-fonts/src/__tests__/index.test.js Outdated
Comment thread packages/gatsby-plugin-preload-fonts/src/prepare/index.js Outdated
@wardpeet wardpeet changed the title feat(gatsby-plugin-preload-fonts): create plugin feat(gatsby-plugin-preload-fonts): fetch loaded fonts Jul 15, 2019
@wardpeet wardpeet changed the title feat(gatsby-plugin-preload-fonts): fetch loaded fonts feat(gatsby-plugin-preload-fonts): preload used fonts Jul 15, 2019
@superhawk610
superhawk610 requested a review from a team July 15, 2019 07:15
@wardpeet wardpeet added the status: awaiting author response Additional information has been requested from the author label Jul 29, 2019
@superhawk610

superhawk610 commented Jul 29, 2019

Copy link
Copy Markdown
Contributor Author

Hey, sorry about the delay, I put this on the backburner until #15475 was addressed but it never was. I'll try and get a commit addressing @wardpeet's most recent feedback this week, hopefully the versioning thing will have sorted itself out.

EDIT: I'm beginning a new job this week, so I haven't had any free time to work on this. I'll get final review changes up as soon as I get the time :)

@wardpeet

Copy link
Copy Markdown
Contributor

Oh yeah, it got fixed. I missed your issue. It would be awesome to get this one shipped 🤗.

@superhawk610
superhawk610 requested a review from wardpeet August 23, 2019 06:01
@superhawk610

Copy link
Copy Markdown
Contributor Author

@wardpeet Should be g2g 🔥

@superhawk610 superhawk610 added status: awaiting reviewer response A pull request that is currently awaiting a reviewer's response and removed status: awaiting author response Additional information has been requested from the author labels Aug 23, 2019
@wardpeet

Copy link
Copy Markdown
Contributor

Awesome! 🔥 I'll fix tests and give this plugin one more try and i'll merge afterwards! Thanks for your hard work. This is going to be awesome!

@wardpeet wardpeet left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤞 I believe this one is going to be the one. Tests node 8 works so. Thanks @superhawk610 for this plugin. Like I said before, this is awesome!

@wardpeet
wardpeet merged commit 5b2a76a into gatsbyjs:master Aug 23, 2019
@wardpeet

Copy link
Copy Markdown
Contributor

@superhawk610 we have published your plugin gatsby-plugin-preload-fonts@1.0.0

@phuoccss

Copy link
Copy Markdown
Contributor

Is this recommended for self-hosted fonts?

@sidharthachatterjee

Copy link
Copy Markdown
Contributor

@universse Yeah, should be beneficial whether self hosted or not

@phuoccss

phuoccss commented Aug 24, 2019

Copy link
Copy Markdown
Contributor

Cause I was trying to run the script "preload-fonts": "gatsby-preload-fonts" and got error

err could not establish a connection with the dev server
     attempted connection to http://localhost:8000/___graphql

So I started dev server, and run the scripts, which gives me this output

// font-preload-cache.json
{
  ...
  "assets": {
    "/": {
      "http://localhost:8000/static/Spectral-ExtraBold-76dd69dbe4b4ddc4f3923bf08e883d43.woff2": true,
      ....
    },
  }
}

This works if fonts are loaded from external sources since you get absolute external urls. That's not the case for self-hosted ones with relative urls. So I guess there needs to be a way to configure the origin. Not sure if i'm missing anything though.

@wardpeet

Copy link
Copy Markdown
Contributor

@universse good catch, you could add a PR to actually check the url if it matches the dev server we strip out the domain.

@phuoccss

Copy link
Copy Markdown
Contributor

I made a PR #17235

@chocobuckle

Copy link
Copy Markdown

Maybe I'm missing something simple, but just curious as to how one is meant to use this with continuous deployment? If I run gatsby develop and then gatsby build locally then all works as expected, but when I push a commit and Netlify starts building my site then it obviously errors with a "err could not establish a connection with the dev server" message. Please explain, as if to an idiot! :)

@superhawk610

Copy link
Copy Markdown
Contributor Author

You'll need to tweak your CD config to run the dev server in the background, then run gatsby-preload-fonts, then kill the dev server once that's complete. I'm not totally sure of the best way to accomplish this on Netlify, perhaps @masives can shed some light?

@nandorojo

Copy link
Copy Markdown
Contributor

@superhawk610 Similar question to @chocobuckle here. Any idea how to get this to work on Gatsby Cloud when I deploy?

@superhawk610

Copy link
Copy Markdown
Contributor Author

@nandorojo I'm not super familiar with Gatsby Cloud configuration, but from the docs it seems like the best way to accomplish this would be to add a postinstall script that does what I suggested above, something like

// package.json
"scripts": {
  "postinstall": "scripts/update-font-preloads.sh"
}

// scripts/update-font-preloads.sh
#!/bin/bash
export CI=1 # Gatsby Cloud may set this for you, in which case
            # it's unnecessary; just make sure it's set or the
            # crawler will hang indefiniely waiting for input

echo "spinning up dev server..."
./node_modules/.bin/gatsby develop &
SERVER_PID=$!

echo "crawling routes to check for font preloads..."
./node_modules/.bin/gatsby-preload-fonts &
CRAWLER_PID=$!

wait $CRAWLER_PID
echo "done!"

kill -9 $SERVER_PID

(I haven't tested this but that's the basic idea)

@botteu

botteu commented Sep 18, 2020

Copy link
Copy Markdown

I'm in the same boat as @chocobuckle . We're hosting on Netlify and the build script is run when we push to branch X, but since the font mapping needs gatsby develop to run, it doesn't work.

@masives

masives commented Sep 21, 2020

Copy link
Copy Markdown
Contributor

@botteu I think you'd need to write a node script that would

  1. Spawn a process with gatsby develop
  2. Wait for gatsby dev server to be available
  3. Run the font preloading script
  4. Kill the server

@botteu

botteu commented Sep 21, 2020

Copy link
Copy Markdown

@masives Yep. But I was hoping to avoid that 😉 But thanks anyways 👍

raffishquartan pushed a commit to raffishquartan/gatsby that referenced this pull request Apr 28, 2026
> Fonts don't start downloading until the browser knows they're needed. Which means that the browser has to download/parse the HTML & CSS and start rendering a section of the page before it knows to start downloading the font.
>
> This can cause significant delays in loading fonts.
>
> A common suggestion is to add a <link preload> to the page for each font that's used on the page so the browser knows to start downloading the font files immediately.

This plugin enables a two-step process for embedding `<link preload>` tags into each statically rendered page in your build output:

1. Run the included puppeteer script to scrape every route in your application and generate a mapping from routes to fonts.
2. Add the plugin to your `gatsby-config.js` and these tags will be automagically embedded into each page at build time.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

status: awaiting reviewer response A pull request that is currently awaiting a reviewer's response

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Preload fonts used on pages

9 participants