ContactSign inSign up
Contact

Loading custom fonts

Browsers can decide to render HTML in multiple passes when custom fonts are used. They do this to speed up the time-to-first-meaningful-paint.

This behavior can cause a test to render without the custom font or use different fonts across repeated runs, making the test unstable. The following techniques can prevent that instability.

Best practice: Fall back to web-safe fonts

Web font loading can vary between browsers, versions, and operating systems. Web-safe fonts are commonly installed by default on browsers and operating systems. We recommend you include a web-safe font in your font stack as a fallback in case your web font isn’t available or doesn’t load as expected.

src/index.css
/* Fallback to "Courier New" for monospace fonts */

code {
  font-family: 'Source Code Pro', 'Courier New', monospace;
}
Which web-safe fonts do you recommend for Chromatic?
  • Sans-serif: Arial, Verdana, Trebuchet MS
  • Serif: Georgia, Times New Roman
  • Monospace: Courier New, Courier
  • Non-Latin scripts: use a web-safe font that covers your target language (e.g., Noto Sans JP for Japanese)

Solution A: Preload fonts

We recommend that you always ensure fonts are loaded before your tests are executed. With Storybook, you can preload fonts by specifying them in ./storybook/preview-head.html.

./storybook/preview-head.html
<link rel="preload" href="path/to/font.woff2" as="font" type="font/woff2" crossorigin="anonymous" />
If you’re loading fonts from an external CDN service (like Google Fonts or Adobe Fonts), be careful that the font files you’re preloading match the fonts called for in your CSS.

Solution B: Point font-face declarations at static files

If your CSS has global @font-face declarations that point to a CDN, you may need to override them to ensure that your snapshots always use assets loaded locally.

For example, you might reference a font CDN in your stylesheets like this:

src/index.css
@font-face {
  font-display: optional;
  font-family: 'YourFont';
  font-style: normal;
  font-weight: normal;
  src: url('https://cdn.yoursite.com/yourfont.woff2') format('woff2');
}

To serve the fonts statically, you first need to put your fonts in a static directory for Storybook. We recommend the ../public directory.

Next, create a yourfontface.css file inside your Storybook configuration directory (that is, .storybook). Use it to reference the local path for your font in the ../public directory.

./storybook/yourfontface.css
@font-face {
  font-display: optional;
  font-family: 'YourFont';
  font-style: normal;
  font-weight: normal;
  /* 👇 Change this to point at the local font path */
  src: url('/yourfont.woff2') format('woff2');
}

Reference the stylesheet in Storybook’s preview-head.html configuration file to load the font from the local path.

./storybook/preview-head.html
<link rel="stylesheet" type="text/css" href="/yourfontface.css">

This technique loads a local font file during development and testing in Storybook. Meanwhile, your users are still loading the font from the CDN in production.

Solution C: Check that fonts have loaded in a loader

This alternate solution uses the browser’s font load API and the isChromatic() helper function to verify that fonts load when in the Chromatic environment.

./storybook/preview.js|ts
import isChromatic from 'chromatic/isChromatic';

// Use the document.fonts API to check if fonts have loaded
// https://developer.mozilla.org/en-US/docs/Web/API/Document/fonts
const fontLoader = async () => ({
  fonts: await Promise.all([document.fonts.load('400 1em Font Name')]),
  // or
  // fonts: await document.fonts.ready,
});

/* 👇 It's configured as a global loader
 * See https://storybook.js.org/docs/writing-stories/loaders
 * to learn more about loaders
 */
export const loaders = isChromatic() && document.fonts ? [fontLoader] : [];

Solution D: Don’t load fonts

As a last resort, you can also disable custom fonts by setting font-display: optional in your CSS when running in Chromatic.