eivindml
eivindml

Reputation: 2520

How to improve font loading/caching with Vercel/Next.js?

I use custom fonts on a website. It works, but it looks like they aren't cached long enough, as I way too often get FOUT. Some times multiple times a day, on production. I'm hoping I can cache fonts for a longer period (or another solution) to prevent this from occurring as often.

Fonts are preloaded in _document.tsx like this:

<link
   rel='preload'
  as='font'
  href='/fonts/Calibre-Regular.woff2'
  type='font/woff2'
  crossOrigin='anonymous'
  />

Then in _app.tsx like this:

<script jsx>{`
 @font-face {
  font-family: 'Calibre';
  font-display: swap;
  src: url('/fonts/Calibre-Regular.woff2') format('woff2'),
    url('/fonts/Calibre-Regular.woff') format('woff');
}
`}</script>

How can I improve this? Or set a longer cache time for the fonts? 🙏🏻

Upvotes: 5

Views: 5903

Answers (2)

LSerni
LSerni

Reputation: 57408

I wouldn't be so sure that FOUTs are caused by the fonts not being cached long enough. Could they be not yet loaded, for example? The preload link should cause the browser to request the font and cache it normally - the WOFF(2) files are static files anyway. Unless special pains are taken to not cache them, they should be cached and stay cached.

So, I would first of all check the request and response headers for those WOFF files, both from the client and the server. What is it exactly that gets sent to the Vercel layer? And, more important, what is it that gets sent from the server to the cache? (I have misplaced enough cache-related pragmas myself that I now always check those first).

If that is OK, and I wouldn't bet on that, I would look at the actual load times and request waterfall from the site during a FOUT. This might point to caching problems or errors in the load sequence (much more rarely, but I have my share of those too).

If nothing shows out at this point, you could try to get rid of FOUTs otherwise - load animations, load delay, or just hiding the text.

But my money is on the server-Vercel leg of the journey, 70% on the server sending borked headers, 30% on Vercel not reacting properly.

A dirty and terrible way to be sure whether it is the cache or something else is to have another machine prod the cache continuously at appropriate intervals from the same network (e.g. a cron job, or even more brutal, a while loop with curl and sleep from a terminal on a commodity Raspberry), at a frequence higher than that of the FOUT occurrences. If that makes the FOUTs disappear, then it might really be the caching time -- but frankly, I'd be really surprised.

Upvotes: 0

kennethormandy
kennethormandy

Reputation: 2150

Without seeing the full example, I’d:

  • confirm first that your React app is server rendered. With Next.js, I’d imagine it is, but the link tag and script (which should be a style tag?) should be part of the initial HTML
  • check if changing the inline CSS into a <link /> tag and separate CSS style that, which only contains the @font-face declarations. Yes, this adds one additional HTTP request, but then that CSS would presumably follow Vercel’s static caching rules and not change for 31 days: https://vercel.com/docs/edge-network/caching#static-files (although it’s unclear here whether the cache is invalidated when you redeploy that file, or when you do any deploy)
  • decide to use something custom to override Vercel’s default cache settings, and add a Routes property in your vercel.json file.

For example, to cache everything in the fonts/ directory for one week (and assuming the new, external CSS file is there as well), the config would be:

{
  "routes": [
    {
      "src": "/fonts/(.*)",
      "headers": { "cache-control": "s-maxage=604800" },
      "dest": "/fonts/$1"
    }
  ]
}

This is based on the config example in the docs: https://vercel.com/docs/configuration#routes/headers

Hope one or more of those together is helpful!

Upvotes: 3

Related Questions