Zach’s ugly mug (his face) Zach Leatherman

A Comprehensive Guide to Font Loading Strategies

July 12, 2016 #3 Popular

This guide is not intended for use with font icons, which have different loading priorities and use cases. Also, SVG is probably a better long term choice.

Updated July 27, 2017 with new information on font-display.
Updated August 14, 2017 with a link to a glossary page.

Jump to:

A diagram describing the relationship between the font loading strategies


If you run into a term that you don’t know, please consult the glossary of web font loading terms I’ve prepared. If a term is missing in the glossary, please let me know on Twitter @zachleat or in the comments.

Quick Guide

If you’re looking for a specific approach, I’ve prepared some handy links that will take you to the section you need. Let’s say you want an approach that:

  • is the most well rounded approach that will be good enough for most use cases: FOUT with a Class.

  • is the easiest possible thing to implement: I’ve learned a lot about web fonts and at time of writing this article the current browser support is lacking for the easiest methods for effective/robust web font implementation. It is with that in mind that I will admit—if you’re looking for the easy way out already, you should consider not using web fonts. If you don’t know what web fonts are doing to improve your design, they may not be right for you. Don’t get me wrong, web fonts are great. But educate yourself on the benefit first. (In Defense of Web Fonts, The Value of a Web Font by Robin Rendle is a good start. If you have others, please leave a comment below!)

  • is the best performance-oriented approach: Use one of the Critical FOFT approaches. Personally, at time of writing my preference is Critical FOFT with Data URI but will shift toward Critical FOFT with preload as browser support for preload increases.

  • will work well with a large number of web fonts: If you’re web font obsessed (anything more than 4 or 5 web fonts or a total file size of more than 100KB) this one is kind of tricky. I’d first recommend trying to pare your web font usage down, but if that isn’t possible stick with a standard FOFT, or FOUT with Two Stage Render approach. Use separate FOFT approaches for each typeface (grouping of roman, bold, italic, et cetera).

  • will work with my existing cloud/web font hosting solution: FOFT approaches generally require self hosting, so stick with the tried and true FOUT with a Class approach.


  1. Ease of Implementation: sometimes, simple is what makes the deadline.
  2. Rendering Performance: FOUT is a feature that will allow you to render fallback fonts immediately and render the web font when it loads. We can take additional steps to reduce amount of time a fallback font is shown and reduce the impact of FOUT or even eliminate it altogether.
  3. Scalability: some font loading approaches encourage serial loading of web fonts. We want the requests to happen in parallel. We’ll evaluate how well each approach works with a growing web font budget.
  4. Future Friendly: will it require additional research and maintenance if a new font format comes out or will it be easily adaptable?
  5. Browser Support: is it sufficient to work with a wide enough base to meet most projects’ browser support requirements?
  6. Flexibility: does the approach easily facilitate grouping web font requests and their repaints and reflows? We want control over which fonts load and when.
  7. Robustness: What happens if a web font request hangs? Will the text be readable or will the web font be a single point of failure (SPOF)?
  8. Hosting: does the approach require self hosting or is it adaptable to work with various web font loaders provided by cloud providers/font foundries?
  9. Subsetting: some font licenses don’t allow subsetting. Some approaches below require subsetting for optimal performance.

Unceremonious @font-face

Throw a naked @font-face block on your page and hope for the best. This is the default approach recommended by Google Fonts.


  • Very simple: add a CSS @font-face block with WOFF and WOFF2 formats (maybe even an OpenType format too, if you want better Android < 4.4 support—Compare WOFF with TTF/OTF on Can I Use).
  • Very future friendly: this is the default web font behavior. You’re in the web font mainstream here. Adding additional font formats is as simple as including another URL in your @font-face comma separated src attribute.
  • Good rendering performance in Internet Explorer and Edge: no FOIT, no hidden or invisible text. I fully support this by-design decision made my Microsoft.
  • Does not require modification of the fonts (through subsetting or otherwise). Very license friendly.


  • Bad rendering performance everywhere else: Maximum three second FOIT in most modern browsers, switches to FOUT if load takes longer. While requests may finish earlier, we know how unreliable the network can be—three seconds is a long time for invisible unreadable content.
  • Not very robust, yet: Some WebKits have no maximum FOIT timeout (although WebKit has very recently fixed this and I believe it will be included with Safari version 10), which means web font requests may be a single point of failure for your content (if the request hangs, content will never display).
  • No easy way to group requests or repaints together. Each web font will incur a separate repaint/reflow step and its own FOIT/FOUT timeouts. This can create undesirable situations like the Mitt Romney Web Font Problem.

Verdict: Do not use.


Add a new font-display: swap descriptor to your @font-face block to opt-in to FOUT on browsers that support it. Optionally, font-display: fallback or font-display: optional can be used if you consider web fonts to be unnecessary to the design. At time of writing, this feature is not available in any stable web browsers. Update: on July 25, 2017 Chrome 60 was released on the Chrome stable release channel which includes support for font-display.


  • Very Simple: Only a single CSS descriptor added to your @font-face block.
  • Good rendering performance: if this approach had ubiquitous browser support, this would give us FOUT without any JavaScript. A CSS-only approach would be ideal.
  • Super future friendly: is orthogonal to web font formats. No other changes are required if you add new formats to your stack.
  • Very robust: a FOUT approach will show your fallback text in supported browsers even if the web font request hangs. Even better—your web fonts are not dependent on a JavaScript polyfill—which means if the JavaScript fails, users are still eligible for the web fonts.
  • Does not require modification of the fonts (through subsetting or otherwise). Very license friendly.


  • Only available on Chrome (version 60+ on Desktop and Android, see Chrome Platform Status). In progress and behind a flag on Firefox Platform Status but not yet documented at all on Edge Platform Status. Until support is ubiquitous across A-grade browsers, developers will need to pair this with a JavaScript approach.
  • In fact, pairing this with a JavaScript approach doesn’t really buy you all that much on an empty-cache load, given that the JavaScript FOUT approaches documented on this page usually require modification of your CSS to avoid using any web fonts prior to font loading classes added by JavaScript. When the two approaches are paired together and the JavaScript fails you won’t get web fonts even though font-display is a CSS-only approach. It’ll help for repeat view optimizations though.
  • Limited flexibility: No way to group requests or repaints. This isn’t as bad as it sounds—if you FOUT everything you’ll avoid the Mitt Romney Web Font problem but grouping can be useful for other reasons—we’ll go into that later.
  • ~Hosting: No control of this property on any known web font host. It’s not included in the Google Fonts CSS, for example. This will probably change when browser support improves.~ Update: in May 2019 Google Fonts added the ability to set a font-display property, related blog post can be read here.

Verdict: Definitely add it to your @font-face blocks, but by itself it’s not sufficient.


Add <link rel="preload" href="font.woff2" as="font" type="font/woff2" crossorigin> to fetch your font sooner. Pairs nicely with an unceremonious @font-face block and feel free to also throw in the font-display descriptor as well for bonus points.

Keep in mind: The pros and cons for this approach are heavily dependent on the font loading strategy you pair it with, whether it be Unceremonious @font-face or font-display.


  • Super easy to implement, one <link> and you’re off.
  • Better rendering performance than a @font-face block. Web fonts are requested higher up in the waterfall.
  • Future friendly if you use the type attribute to specify the font format. At this point it’s still possible (although it looks unlikely) that a web browser will implement preload before WOFF2 for example, and without this attribute you could be looking at a wasted request. So, make sure you include type.
  • Does not require modification of the fonts (through subsetting or otherwise). Very license friendly.


  • Scalability: The more you preload, the more you can block initial render (note data for this comparison was gathered on a site that was using Critical CSS). Try to limit usage to the most important one or two web fonts.
  • Limited browser support—only Blink support right now, but more coming soon.
  • Flexibility: no way to group repaints/reflows.
  • You probably wouldn’t be able to use this with a third party host. You’d need to know at markup render the URL of the web font you’re requesting. Google Fonts, for example, generates these in the CSS request you make to their CDN.

Verdict: Not sufficient by itself.

Don’t use Web Fonts

I won’t go into this approach too much because, well, it isn’t technically a font loading strategy. But I will say that it’s better than using web fonts incorrectly. You are missing out on many new typographic features and improvements in readability that a web font can give you, but it is your option to opt-out.


  • Not sure it could be simpler: just use font-family without @font-face.
  • Near instant rendering performance: No worries about FOUT or FOIT.


  • Limited availability. Very few system fonts are available cross platform. Check to see if a system font has acceptable browser support for your needs.

Verdict: Sure, I guess, but I wouldn’t be excited about it.

Inline Data URI

There are typically two kinds of inlining covered by this method: in a blocking <link rel="stylesheet"> request or in a <style> element in the server rendered markup. Both (two web fonts embedded in a blocking CSS request) and (seven web fonts) use this approach.


  • Seemingly great rendering performance: this approach has no FOUT or FOIT. This is a big deal.
  • Flexibility: Don’t need to worry about grouping repaints/reflows—this approach has no FOUT or FOIT.
  • Robustness: inlining puts all your eggs into your initial server request basket.


  • A catch with rendering performance: while this approach doesn’t FOUT, it can significantly delay initial render time. On the other hand it will render “finished.” But keep in mind that even a single WOFF2 web font is probably going to be around 10KB—15KB and inlining just one as a Data URI will likely take you over the HTTP/1 recommendation of only having 14KB or less in the critical rendering path.
  • Browser support: Doesn’t take advantage of the comma separated format list in @font-face blocks: this approach only embeds one format type. Usually in the wild this has meant WOFF, so using this method forces you to choose between ubiquity (WOFF) and much narrower user agent support but smaller file sizes (WOFF2).
  • Bad scalability: Requests don’t happen in parallel. They load serially.
  • Self hosting: Required, of course.

Verdict: Only use this method if you really despise FOUT—I wouldn’t recommend it.

Asynchronous Data URI Stylesheet

Use a tool like loadCSS to fetch a stylesheet with all of the fonts embedded as Data URIs. Often you’ll see this coupled with a localStorage method of storing the stylesheet on the user agent for repeat views.


  • Rendering performance: Mostly eliminates FOIT (see note in the Cons)
  • Flexibility: Easy to group requests into a single repaint (put multiple Data URIs in one stylesheet).
  • Ease: Does not require any additional CSS changes to use. This is a big benefit. However, implementation isn’t all candy and roses.
  • Robust: If the asynchronous request fails, fallback text continues to be shown.


  • Rendering performance: Has a very noticeable, but short FOIT while the stylesheet and Data URIs are being parsed. It’s quite distracting. I see this method often enough that I can recognize the approach without looking into the source code.
  • Flexibility and Scalability: Grouped requests and repaints are coupled together. If you group multiple Data URIs together (which will cause loading to occur in serial and not in parallel), they will repaint together. With this method, you can’t load in parallel and group your repaints.
  • Not maintenance friendly. Requires you to have your own method to determine font format support. Your JavaScript loader will need to determine which font format is supported (WOFF2 or WOFF) before fetching the Data URI stylesheet. This means if a new font format comes out, you’ll need to develop a feature test for it.
  • Browser support: You can bypass the maintenance of the loader step and hard-code to WOFF2 or WOFF but this will either incur larger than necessary or potentially throwaway requests (the same drawback we talked about for Inline Data URIs).
  • Self Hosting: Required.

Verdict: It’s OK but we can do better.

FOUT with a Class

Use the CSS Font Loading API with a polyfill to detect when a specific font has loaded and only apply that web font in your CSS after it has loaded successfully. Usually this means toggling a class on your <html> element. Use with SASS or LESS mixins for easier maintenance.


  • Rendering performance: Eliminates FOIT. This method is tried and tested. It’s one of the approaches recommended by TypeKit.
  • Flexibility: Easy to group requests into a single repaint (use one class for multiple web font loads)
  • Scalability: Requests happen in parallel
  • Robust: if the request fails, fallback text is still shown.
  • Hosting: Works independent of font loader (easy to implement with third party hosts or with existing @font-face blocks)
  • Great browser support, polyfills typically work everywhere that web fonts are supported.
  • Future friendly: polyfills aren’t coupled to font formats and should work with existing @font-face blocks. That means when a new format comes out, you can just change your @font-face as normal.
  • Does not require modification of the fonts (through subsetting or otherwise). Very license friendly.


  • Requires strict maintenance/control of your CSS. A single use of a web font font-family in your CSS without the guarding loaded class will likely trigger a FOIT.
  • Typically requires you to hard code which web fonts you want to load on the page. This can mean that you end up loading more web font content than a page needs. Remember that with unceremonious @font-face usage, newer browsers only download web fonts that are actually used on your page. This is given to you for free. This is why the New York Times can get away with 100 different @font-face blocks on their home page—the browser only downloads a fraction of those. With this approach, you must tell the browser which fonts to download independent of usage.

Verdict: This is the baseline standard. This will work for most use cases.

FOFT, or FOUT with Two Stage Render

This approach builds on the FOUT with a Class method and is useful when you’re loading multiple weights or styles of the same typeface, e.g. roman, bold, italic, bold italic, book, heavy, and others. We split those web fonts into two stages: the roman first, which will then also immediately render faux-bold and faux-italic content (using font synthesis) while the real web fonts for heavier weights and alternative styles are loading.


  • All the existing Pros of the FOUT with a Class approach.
  • Rendering performance: Greatly reduces the amount of content jumping that occurs when the web font has loaded. Given that we divide our web font loads into two stages, this allows the first stage (the roman font—the one that will incur the most reflow) much quicker than if we had grouped all our fonts together into a single repaint.


  • All the existing Cons of the FOUT with a Class approach.
  • Some designers are highly allergic to font synthesis. Objectively, synthesized variations are less useful than their real counterparts but that isn’t a fair comparison. Keeping in mind that the synthesized versions are only a temporary placeholder, the question we need to ask is: are they more or less useful than the fallback font? More. The answer is more.

Verdict: Great for those interested in extra performance but can’t subset with Critical FOFT.

Critical FOFT

The only difference between this method and standard FOFT approach is that instead of the full roman web font in the first stage, we use a subset roman web font (usually only containing A-Z and optionally 0-9 and/or punctuation). The full roman web font is instead loaded in the second stage with the other weights and styles.


  • All the existing Pros of the FOFT approach
  • Rendering performance: The first stage loads even faster (more noticeable on slower connections) further minimizing the time to first stage web font repaint, making your most used web font reflow occur sooner rather than later.


  • All the existing Cons of the FOFT approach.
  • Introduces a small amount overhead in that the subset roman font loaded in the first stage is duplicated by the full roman web font loaded in the second stage. This is the price we’re paying to minimize reflow.
  • License restriction: Requires subsetting.

Verdict: Use one of the improved Critical FOFT variations below.

Critical FOFT with Data URI

This variation of the Critical FOFT approach simply changes the mechanism through which we load the first stage. Instead of using our normal font loading JavaScript API to initiate a download, we simply embed the web font as a inline Data URI directly in the markup. As previously discussed, this will block initial render but since we’re only embedding a very small subset roman web font this is a small price to pay to mostly eliminate FOUT.


  • All the existing Pros of the Critical FOFT approach.
  • Eliminates FOIT and greatly reduces FOUT for the roman font. A small reflow will occur for additional characters loaded in the second stage and when the other weights and styles are loaded, but it will have a much smaller impact.


  • All the existing Cons of the Critical FOFT approach.
  • The small inlined Data URI will marginally block initial render. We’re trading this for highly reduced FOUT.
  • Self hosting: Required.

Verdict: This is the current gold standard, in my opinion.

Critical FOFT with preload

This variation of the Critical FOFT approach simply changes the mechanism through which we load the first stage. Instead of using our normal font loading JavaScript API to initiate a download, we use the new preload web standard as described above in the preload method. This should trigger the download sooner than previously possible.


  • All the existing Pros of the Critical FOFT approach.
  • Rendering performance: Downloads should trigger higher up in the waterfall than with previous methods. I’d guess this is even more dramatic with HTTP headers but haven’t yet confirmed this hunch. This method is better than Critical FOFT with Data URI in that it can use the browser cache for repeat requests, rather than re-requesting the same web font data with every server markup request.


  • All the existing Cons of the Critical FOFT approach.
  • Use only with a single web font format.
  • As stated above, browser support is limited—only Blink at time of writing.
  • preload can marginally delay initial render (note data for this comparison was gathered on a site that was using Critical CSS)
  • Self hosting: Probably required.

Verdict: This will be the new gold standard when browser support improves.

< Newer
Beyond Code #4 at NEJS CONF 2015
Older >
CSS Position Sticky

Zach Leatherman IndieWeb Avatar for a builder for the web and the creator/maintainer of IndieWeb Avatar for https://www.11ty.devEleventy (11ty), an award-winning open source site generator. At one point he became entirely too fixated on web fonts. He has given 81 talks in nine different countries at events like Beyond Tellerrand, Smashing Conference, Jamstack Conf, CSSConf, and The White House. Formerly part of CloudCannon, Netlify, Filament Group, NEJS CONF, and NebraskaJS. Learn more about Zach »


IndieWeb Avatar for https://chrispederick.comChris J. ZähllerChris J. ZähllerRigorChris Ferdinandi ⚓️Tammy Everts at #JAMstackconfIndieWeb Avatar for https://webdesign.idjgie.xyzIndieWeb Avatar for https://www.smashingmagazine.comIndieWeb Avatar for https://allprowebdesigns.comIndieWeb Avatar for https://www.webmastersgallery.comIndieWeb Avatar for https://smartphonephotography.inIndieWeb Avatar for http://unsorted.coIndieWeb Avatar for http://ddrv.cnIndieWeb Avatar for http://ddrv.cnIndieWeb Avatar for https://www.maiyewang.comIndieWeb Avatar for http://anna.borsaendeks.comIndieWeb Avatar for http://anna.borsaendeks.comIndieWeb Avatar for http://anna.borsaendeks.comIndieWeb Avatar for http://anna.borsaendeks.comIndieWeb Avatar for https://matomosci.wordpress.comIndieWeb Avatar for https://annualbeta.comIndieWeb Avatar for http://ddrv.cnIndieWeb Avatar for https://workingdraft.deIndieWeb Avatar for https://fasterizestag.wpengine.comIndieWeb Avatar for https://www.fasterize.comIndieWeb Avatar for http://wp.desakami.idIndieWeb Avatar for https://blog.linkody.comIndieWeb Avatar for https://fasterizeblog.staging.wpengine.comjin_fuGabriel NDavid HellmannSmashing MagazineKabir PatelSmashing MagazineKabir PatelLautaro Ferreyrojer clarkeReubenJosé M Casani Guerra𝙳𝚊𝚟𝚎 𝙰𝚜𝚙𝚒𝚗𝚊𝚕𝚕 ⚛️KarlSøren Birkemeyer 🦊Ben Greenberg 🧢 Autonomous Zone 🧢Zell Liew 🤗julien boubelDuduman Bogdan VladIndieWeb Avatar for https://prema.kapilaya.netIndieWeb Avatar for https://imjohnainsworth.comIndieWeb Avatar for https://www.yogitatrainingcenter.comIndieWeb Avatar for https://www.splendidwebsites.comIndieWeb Avatar for https://prema.kapilaya.netIndieWeb Avatar for https://www.splendidwebsites.comIndieWeb Avatar for https://br.atsit.inIndieWeb Avatar for https://molikovdesign.websiteIndieWeb Avatar for https://www.seowebdesignllc.comIndieWeb Avatar for https://wpwerk.comIndieWeb Avatar for https://business2020.xyzIndieWeb Avatar for https://gsensenews.comIndieWeb Avatar for https://docuneedsph.comIndieWeb Avatar for Avatar for http://adharidshop.liveIndieWeb Avatar for https://www.routech.roIndieWeb Avatar for http://marketingtumbler.comIndieWeb Avatar for https://webdesigntips.blogIndieWeb Avatar for https://wpwerk.comIndieWeb Avatar for Avatar for http://blog.michaelogrady.netIndieWeb Avatar for https://susmartech.comIndieWeb Avatar for https://www.seowebdesignllc.comIndieWeb Avatar for https://www.smashingmagazine.comIndieWeb Avatar for https://business2020.xyzIndieWeb Avatar for http://adharidshop.liveIndieWeb Avatar for https://gsensenews.comIndieWeb Avatar for http://niclink.irIndieWeb Avatar for https://hostulum.comIndieWeb Avatar for https://calendar.perfplanet.comIndieWeb Avatar for https://rssfeeds.cloudsite.buildersIndieWeb Avatar for http://lookfor168.cnIndieWeb Avatar for https://711web.comIndieWeb Avatar for https://711web.comJames RobinsonIndieWeb Avatar for https://imjohnainsworth.comIndieWeb Avatar for https://xwp.coIndieWeb Avatar for https://codedigger.caZell Liew 🤗IndieWeb Avatar for https://nexstair.comJoan León 🏞⚡️Itamar Turner-TrauringIndieWeb Avatar for https://www.splendidwebsites.comIndieWeb Avatar for https://www.dubaicitycompany.plIndieWeb Avatar for https://www.sandippandey.inIndieWeb Avatar for http://winsweepstakes.xyzIndieWeb Avatar for https://sahafi.xyzIndieWeb Avatar for https://www.sweetsaw.xyzIndieWeb Avatar for https://cnwicker.comIndieWeb Avatar for https://itpcb.comIndieWeb Avatar for https://cloudfour.comIndieWeb Avatar for https://itpcb.comIndieWeb Avatar for https://joomlanutshell.comIndieWeb Avatar for https://editor.sitepoint.comIndieWeb Avatar for https://www.smashingmagazine.comIndieWeb Avatar for https://tsdigital.caIndieWeb Avatar for https://br.atsit.inIndieWeb Avatar for https://gsensenews.comIndieWeb Avatar for https://blog.avangartiste.comIndieWeb Avatar for https://www.thesocialreaders.comIndieWeb Avatar for https://bootstraphunter.comIndieWeb Avatar for https://flmarket.ruIndieWeb Avatar for Avatar for https://wawas-kingdom.comIndieWeb Avatar for https://calibreapp.comIndieWeb Avatar for http://studio-rgb.ruIndieWeb Avatar for https://kometa73.ruIndieWeb Avatar for https://imaginemarketingonline.esIndieWeb Avatar for https://www.vpsman.netIndieWeb Avatar for https://imaginemarketingonline.esIndieWeb Avatar for Avatar for https://webdesigntips.blogIndieWeb Avatar for https://css-tricks.comIndieWeb Avatar for https://codestream.tkIndieWeb Avatar for https://softwaremile.comIndieWeb Avatar for https://711web.comIndieWeb Avatar for https://docuneedsph.comIndieWeb Avatar for https://br.atsit.inIndieWeb Avatar for https://w.vpsman.netIndieWeb Avatar for https://w.vpsman.netIndieWeb Avatar for https://nice-space.comCraig WebbIndieWeb Avatar for https://nicksimson.comadriel zarateIndieWeb Avatar for https://nicolasfriedli.chIndieWeb Avatar for https://nicolasfriedli.chIndieWeb Avatar for https://editor.sitepoint.comIndieWeb Avatar for Avatar for https://www.clever-age.comIndieWeb Avatar for https://thelinuxcode.comIndieWeb Avatar for https://www.gingerdoc.comIndieWeb Avatar for https://p4m.techIndieWeb Avatar for https://p4m.techIndieWeb Avatar for https://boldleap.coIndieWeb Avatar for https://russian-crm.ruIndieWeb Avatar for http://www.itfaba.comIndieWeb Avatar for https://ryanccn.devIndieWeb Avatar for https://onlinetechexplore.comIndieWeb Avatar for https://speckyboy.comIndieWeb Avatar for https://prospeedguy.comIndieWeb Avatar for https://wordpress-510621-2976382.cloudwaysapps.comIndieWeb Avatar for https://geekcarrot.orgIndieWeb Avatar for https://blog.logrocket.comIndieWeb Avatar for https://wons.storeIndieWeb Avatar for https://aruf.ruIndieWeb Avatar for Avatar for https://editor.sitepoint.comIndieWeb Avatar for https://www.cloudways.comIndieWeb Avatar for https://codesolution.infoIndieWeb Avatar for https://www.cursuswp.comIndieWeb Avatar for https://cloudfour.comIndieWeb Avatar for https://getridbug.comIndieWeb Avatar for https://fenq.comIndieWeb Avatar for https://idushu.comIndieWeb Avatar for https://www.wdssr.comIndieWeb Avatar for https://hashtagbeginner.comIndieWeb Avatar for https://lobste.rsIndieWeb Avatar for https://hashtagbeginner.comIndieWeb Avatar for https://bogos.xyzIndieWeb Avatar for https://bogosa.xyzIndieWeb Avatar for https://sakhin.xyzIndieWeb Avatar for https://kasita.xyzIndieWeb Avatar for https://bogosa.xyzIndieWeb Avatar for https://limada.xyzIndieWeb Avatar for https://swalif.xyzIndieWeb Avatar for https://nice-space.comIndieWeb Avatar for Avatar for Avatar for Avatar for https://wpjohnny.comIndieWeb Avatar for https://www.wpfastworld.comIndieWeb Avatar for http://www.pricl.comIndieWeb Avatar for https://tuatphukien.comIndieWeb Avatar for https://www.ozhipin.comIndieWeb Avatar for


Kyle Mitofsky #BLMLara HoganMayankÁlvaro MontoroTiro TypeworksMax CroftsMike BifulcoGeorge FrancisFlorian GeierstangerMike AparicioKeith FreundJess Peck 🐍🤖Jody DonettiAlex MacArthur👾 Attila GondaChristian Medina

1 Bookmark

IndieWeb Avatar for
  1. Gabriel N


    Yes you did, and the context is lost. Just being able to rollover —or soft click on mobile — to view what the acronym stands for would be great. Some kind of sidenote with the direct link to the glossary the first time the term appears would be better.… Truncated

  2. Zach Leatherman


    how many fonts you got

  3. Søren Birkemeyer 🦊


    Usually two typefaces for client projects, totaling 4-5 fonts (reg/bold of a display typeface, reg/bold/italic of the text work horse).

  4. Stefan Judis


    I'm still recommending it. :D 🙈

  5. Mayank


    every time I'm on your blog for some other reason, I still click that one just to bump up the count

  6. Manuel Matuzović


    All time classic!

  7. Keith Freund


    I've read it a zillion times and when I hire new people, it's one of the 3 performance articles I ask everyone to read, along with these:……

  8. Sia Karamalegos


    Similarly, this is mine. Originally written a while ago but I try to keep it updated. I had to add the year to the title to beat my own search ranking with the Medium version even though I set a canonical link.…

  9. Nicolas Hoizey


    This is still my main destination when I want to find informations about font loading, reading at least parts of it 3 or 4 times a year, I guess. 👍

  10. Dash 🍜


    What is FOUT/FOFT/FOIT?

  11. Nicolas Hoizey


    Je ne dirais pas que ˋfont-loading` remplace complètement l’API. Elle reste utile pour gérer des applications synchronisées de multiples fontes, par exemple :…

  12. Yvain Liechti


    Oui evidement, avant font-display le js était obligatoire pour réaliser l'équivalent. L'API est plus complete et permet plus que ça.

  13. Keith Freund


    Usually for an above-the-fold font weight or two via one of the strategies covered in this page:…

  14. Akash Chavda 🎯


    Want to learn more about optimizing CSS for performance? Check out these resources 👇…

  1. Shaun O'Connell Disqus

    13 Jul 2016
    Hey Zach,Have you done any research into ServiceWorkers for font-loading? Also how does HTTP2 affect font-loading?It always feels weird to me to be reliant on JavaScript to fix what is a CSS-driven problem. i.e. go to step 3 (parse JS, execute) to fix a problem at step 2 (parse CSS, paint). Is it worth mentioning (or factoring in) that being reliant on JS is a CON for the strategies that use JS?Thanks for the insightful article!
    1. zachleat Disqus

      14 Jul 2016
      You’re really tee-ing up some great future article ideas here :)ServiceWorkers could improve the repeat-view optimization, I think, but wouldn’t do much for first-load would it? I’m not sure yet, I will play around with it!I totally agree about your second point. Unfortunately all the workable, usable methods today rely on JS. The article will likely change a lot when `font-display` support increases, and rather than put it in every other method I put a few notes on that in the `font-display` method.Thanks for your comment!
    2. Jonathan Bidston Disqus

      26 Sep 2016
      Regarding HTTP2, I've been having a look at getting the initial HTML as small as possible so that I can use Push to send a highly compressed font file along with it (filling the idle network time).Ideally, I'd then cache the font file and set a cookie to prevent further pushes with requests (or something along those lines).
  2. Alex Bell Disqus

    13 Jul 2016
    Nice roundup. Is there any evidence for your statement that "preload can marginally delay initial render"? The preload spec says asset load is non-render-blocking. Preload seems like the least blocking solution other than no fonts at all. Also, Opera 38 is currently stable, and shipping CSS `font-display`. I'd like to see them get some credit!
    1. zachleat Disqus

      14 Jul 2016
      I measured the numbers on my own site and documented it here: Note those numbers are using Network Throttled to 3G and used on a site that has a large number of inline/critical CSS at the top. Yoav Weiss (implemented preload on Blink) has seen the article, so he’s at least aware of it.
    2. zachleat Disqus

      14 Jul 2016
      The preload browser support bullet says Blink support—which includes Chrome and Opera! I tried to save some typing 😎
      1. Alex Bell Disqus

        15 Jul 2016
        My point was that your statement about CSS font-display "At time of writing, this feature is not available in any stable web browsers." is inaccurate. Opera 38 is stable, and has the feature.
        1. zachleat Disqus

          15 Jul 2016
          Oh, sorry! I thought you were talking about preload. Although I did just test the font-display demo in Opera 38 and I don’t think it’s supported. Can you confirm?
          1. Ramanan V Disqus

            24 Jul 2016
            I think it's disabled by default but that's just like Chrome. You can enable it in opera://flags and choosing "Enable experimental Web Platform features"About your tests, while it's true that multiple preload fonts can slow down, but that's in pages without images. Typical pages will have images and if only one font is preloaded, the page will start downloading images even if the priority for images is slow.
            1. Ramanan V Disqus

              24 Jul 2016
              Also agree "render-blocking" misleading. Let's say you inline styles (and load the full style asynchronously such as using requestAnimationFrame) and preload ... it doesn't block the render. It just gives a FOIT. But using it together with font-display such as "swap" will lead to immediate render, once the browser reads the inline css and some HTML.
  3. James Deering Disqus

    14 Jul 2016
    Open font, remove unneeded glyphs, base64 font, insert in @font-face src: url, you're done. Complexity not needed.
    1. zachleat Disqus

      14 Jul 2016
      For others, this discussion was continued here:
    2. Shaun O'Connell Disqus

      14 Jul 2016
      I prefer that technique too James, that is, until I saw what a massive impact it had to the Time To First Render, along with some of the side-effects Zach mentions above.Use to test this strategy against a post-load ('ASYNCHRONOUS DATA URI STYLESHEET') strategy and then compare the two. Hopefully it'll be as illuminating for you as it was for me.
  4. Mit10 Disqus

    15 Jul 2016
    Not sure what all the acronyms mean :|I know flash of unstyled text, but not the rest. I can Google them but figured I'd recommend defining them in such an article.
  5. Tom Disqus

    17 Jul 2016
    I might've missed a link to an explanation somewhere on the page, but here are what the acronyms stand for:- FOUT: Flash of Unstyled Text- FOIT: Flash of Invisible Text(from )
    1. zachleat Disqus

      14 Aug 2017
      I’ve created a glossary of terms for this page here:
  6. Serge Zarouski Disqus

    17 Jul 2016
    Hi Zach, thanks for very detailed article! I run a few tests to compare inline vs async vs foft with data uri and here are results: - inline - async - foft with data uriObviously async is a leader for both first and repeat views, but there is always FOUT.FOFT is second fastest, but there is following funny effect in case when network is slow: https://cloud.githubusercon.... There is also a chance that session storage and cache will get out of sync: https://cloud.githubusercon.... That might happen in case when user is using chrome and choose an option to 'Continue where I left off' (see, which is a pretty common case, but also cleaned website data - for first view on slow network they'll get both FOIT & FOUT.Inline is slowest, but most reliable in all cases.So, I modified your approach to FOFT and combined it with inline font and it seems to be most effective:, code is here https://fan-giver.hyperdev..... I removed sessionStorage to prevent FOIT, and FOUT is still there but is less annoying. The only problem will be with narrow fonts. Also instead of relying on sessionStorage I wonder if we can come up with a solution that uses js to determine width of the element, which will be different based on which font was being used to render it at the given moment. That we can find out if we can apply classname to the body earlier.
    1. zachleat Disqus

      05 Aug 2016
      Hey Serge: thank you for your great comment! I’m sorry to tell you that unfortunately there was an error on a few of the demos that might render your data inaccurate. It was fixed about 8 days after your comment was posted. So sorry about thatMore info:
  7. Ying Zhang Disqus

    23 Jul 2016
    Have you thought about license issues with subsetting a web font? At least google fonts do not allow subsetting their fonts.
    1. zachleat Disqus

      16 Dec 2016
      I believe this Google Fonts feature is in Beta:
      1. ZenMaster Disqus

        27 Dec 2016
        Wouldn't it mean that you need to consume it from Google CDN and not self-host (so - inlining it wouldn't work)?
        1. zachleat Disqus

          14 Aug 2017
          You can download Google web fonts to self host and inline on your own site.
  8. Tracker1 Disqus

    27 Jul 2016
    One niggle... when you have variants of the same font, don't name them separately, keep the font name the same with the different attributes for the font...@font-face {font-family: Lato;src: ...urls for regularfont-weight: 400;font-style: normal;}@font-face {font-family: Lato;src: ... urls for boldfont-weight: 700;font-style: normal;}Then when you specify font-family, juse use Lato, and the closest match will be used... bold will use the bold version, normal weight will use regular... same for italics, etc.
    1. zachleat Disqus

      28 Jul 2016
      @tracker1:disqus This isn’t a very well known thing, but font loading polyfills do not work well with that style of @font-face block. See
  9. Peter Y. Chuang Disqus

    31 Jul 2016
    Hi Zach,I think I've encountered the same problem as described by Serge Zarouski, in that the use of session storage somehow produces FOUT and FOIT in repeated views. I am able to reproduce that on Google Chrome without throttling down the network speed.Interestingly, the session storage method works perfectly on Firefox.
    1. zachleat Disqus

      05 Aug 2016
      If you have the devtools open, it could be possible that your cache is disabled. So if you do a refresh with cache disabled, the sessionStorage incorrectly expects a primed cache. It’s unlikely a user will encounter this scenario.
      1. Peter Y. Chuang Disqus

        15 Aug 2016
        I don't think that's the cause of the problem.I tested your demo page on webpagetest and captured videos on Firefox and Chrome. I throttled down the internet to 3G to show it more clearly.This is the result on Firefox: this is on Chrome: Firefox and Chrome didn't download the font files on repeated viewing, as shown on the waterfall charts, so those files are presumably cached.The video of the repeated viewing on Firefox shows no change in fonts, but the video on Chrome shows a very quick change at about 1.5-1.6s mark.In my test on my own website, it appears that Chrome briefly shows the fallback font on the repeated viewing before switching to the correct font. In the process of switching, FOIT sometimes occurs.
      2. Chris J. Zähller Disqus

        22 Apr 2017
        [posted comment on wrong thread]
  10. chhola Disqus

    25 Aug 2016
    I don't understand why FOUT is a good thing now. Haven't we been trying to fix this before by hiding or making the text invisible? But now we are doing the opposite, or am I missing something?
    1. Francis Boudreau Disqus

      31 Jan 2017
      Welcome to the designers vs developers debate!
  11. Tommy Mathiesen Disqus

    08 Sep 2016
    With Chrome and Safari I am experiencing long FOIT in the example where it should only be FOUT:
    1. zachleat Disqus

      08 Sep 2016
      Have a look at this comment:
      1. Tommy Mathiesen Disqus

        08 Sep 2016
        Yes that could have been it, but the FOIT happens with both developer tools on and off - also on my iPhone without any developer mode whatsoever - just a slow 3g connection and then its easy to see.
        1. zachleat Disqus

          08 Sep 2016
          Yeah, it’s independent of whether or not devtools are on/off. It’s dependent on specific sessionStorage keys, which are cache independent. You can clear them out inside of the devtools.
  12. Chris J. Zähller Disqus

    05 Oct 2016
    Zach, what's your opinion on CDN vs. locally hosted font files with Critical FOFT with Data URI? I've read it depends on the locations of the server, the CDN, and the client, so let's assume the webserver is in the central US, the CDN is Google Fonts, and the client is somewhere in CONUS.Also, does it make sense to inline the non-subsetted @font-face declarations and css if you are serving over HTTP2? I've read that inlining is bad practice over HTTP2.
    1. zachleat Disqus

      16 Dec 2016
      These are great questions—I think they’ll require additional analysis before I could give you a solid opinion. Good candidates for future blog posts!
      1. Chris J. Zähller Disqus

        16 Dec 2016
        I look forward to your future thoughts on this.
      2. Chris J. Zähller Disqus

        22 Apr 2017
        Have you worked this out yet? For now, I'm sticking to your strategy, but it would be good to know if I should be thinking of modifying it.BTW, I wrote some PHP to watch my font folder and dynamically update the @font-face declarations in the document head. Still have to update the font observer manually, but in theory that could be automated, as well. Happy to share if you are interested.
        1. zachleat Disqus

          27 Apr 2017
          Not yet!and yeah! Feel free to post a link to a repo if it’s sharable!
  13. MD Yusuf Disqus

    25 Oct 2016
    Hi Zach if I do not have any web hosting server, so can I use Roman web fonts?
    1. zachleat Disqus

      14 Dec 2016
      FOUT with a Class should work pretty easily with Google Web Fonts. TypeKit describes how to do it with their loader too on their documentation.
  14. Francesco Bedussi Disqus

    22 Nov 2016
    Hi Zach,sorry for joining late in the discussion.What about a method as simple as declearing a web font and a safe fallback via css:body { font-family: 'Bungee Inline', cursive;}and then inject the link to load the webfont via JS at page load/document ready:window.addEventListener('load', function(){ var link = document.createElement('link'); link.rel = "stylesheet"; link.href=""; document.head.appendChild(link);})isn't it the same as FOUT with a class with much less fuss?
    1. zachleat Disqus

      22 Nov 2016
      I have a blog post about this approach in the drafts—stay tuned!
    2. zachleat Disqus

      14 Dec 2016
      I’ve detailed the problems with that approach here:
  15. Johny Varsami Disqus

    08 Dec 2016
    Seriously? As if there are not enough other problems in the front end. This is definitely an issue to be solved by the browser.
    1. zachleat Disqus

      30 Jan 2017
      Pray, fine—but keep rowing to shore.
  16. Francis Boudreau Disqus

    30 Jan 2017
    I did some research but can't find the answer : do Typekit automaticaly fix the FOIT problem?
    1. zachleat Disqus

      30 Jan 2017
      Yes, if you use the advanced embed!
      1. Francis Boudreau Disqus

        30 Jan 2017
        But there is no FOIT when I use the "standard" embed. When I load my website, I can see the text before the font is loaded. When the font is loaded (in the network tab), the text changes to the good font. (Is it because I have the font locally on my machine?)
  17. Luke Cavanagh Disqus

    21 Jul 2017
    Really solid post, thank you for sharing.
  18. nil2 Disqus

    23 Aug 2017
    How about a `<link>` tag with an `async` attribute?
  19. Duncan Wilcox Disqus

    26 Aug 2017
    In the critical foft+preload you mention that preload "browser support is limited", but also results could be "more dramatic with HTTP headers", suggesting you haven't tried. It would seem that preload HTTP headers, and their interpretation as push in HTTP2 context, are actually supported by many more browsers than link-preload. Even though some browsers still have some caching glitches of pushed resources on repeat load, it would seem that pushing a subset woff could be higher up in the waterfall and significantly improve performance.
Shamelessly plug your related post

These are webmentions via the IndieWeb and

Sharing on social media?

This is what will show up when you share this post on Social Media:

How did you do this? I automated my Open Graph images. (Peer behind the curtain at the test page)