Local font preloading broken in Storybook 6.2.x with webpack 5 builder
See original GitHub issueDescribe the bug
It seems that preloading fonts from a local static directory in .storybook/preview-head.html is broken as of Storybook 6.2. This is reproducible both with the ~default Webpack 4 and~ new Webpack 5 builder. It’s not reproducible in Storybook 6.1.x, or on Storybook 6.2.x with Webpack 4.
Example preview-head.html
For a file located at /public/Inter.woff2, and with Storybook run with ./public as a --static-dir:
<link
rel="preload"
href="/Inter.woff2"
as="font"
type="font/woff2"
crossorigin
/>
There’s a workaround for the bug in development builds (change the URL to start with /static/media/public/), but production builds additionally don’t work because Storybook inserts a hash in the filename now (see repro steps below).
Let me know if there’s any other info I could provide that’d be helpful for debugging!
To Reproduce
Minimal Repro Repo
- Minimal repro (6.2): https://github.com/mnquintana/storybook-font-preload-repro/tree/storybook%406.2-webpack5-minimal-repro
- Last known good state (6.1): https://github.com/mnquintana/storybook-font-preload-repro/tree/storybook%406.1
Prerequisites
- Make sure you have a webfont in a local directory in your project (e.g.
/public/<font>.woff2) - Make you’re passing in the path to the static directory to both storybook executables (e.g.
start-storybook --static-dir ./publicandbuild-storybook --static-dir ./public) - Make sure you have a
.storybook/preview-head.htmland it includes this<link>:
<link
rel="preload"
href="/<font>.woff2"
as="font"
type="font/woff2"
crossorigin
/>
- Make sure you have a CSS file that references the font file by relative path on desk (e.g.
/styles/global.css):
@font-face {
font-family: "Custom";
src: url("../public/Inter.woff2") format("woff2");
}
- Make sure you have a component that uses the custom family in its styles
- Make sure you’re using
@storybook/builder-webpack5and have{ core: { builder: 'webpack5' } }set in your.storybook/main.js
Production
- Run
build-storybook --static-dir ./public - Launch the production build of storybook
- Open devtools once Storybook launches, and reload the page
- Filter by Fonts in the Network panel
Expected
<font>.woff2 is successfully preloaded from /<font>.woff2.
Actual
<font>.woff2is not successfully preloaded from/<font>.woff2because the CSS file fetches from a different path –/<font>.<hash>.woff2.- e.g.
/Inter.ce6ee6f8.woff2
- e.g.
System
Please paste the results of npx sb@next info here.
Environment Info:
System:
OS: macOS 10.15.7
CPU: (16) x64 Intel(R) Core(TM) i9-9980HK CPU @ 2.40GHz
Binaries:
Node: 15.11.0 - ~/.nvm/versions/node/v15.11.0/bin/node
Yarn: 1.22.10 - /usr/local/bin/yarn
npm: 7.10.0 - ~/.nvm/versions/node/v15.11.0/bin/npm
Browsers:
Chrome: 89.0.4389.128
Firefox: 86.0
Safari: 14.0.3
npmPackages:
@storybook/addon-actions: ^6.2.8 => 6.2.8
@storybook/addon-essentials: ^6.2.8 => 6.2.8
@storybook/addon-links: ^6.2.8 => 6.2.8
@storybook/builder-webpack5: ^6.2.8 => 6.2.8
@storybook/react: ^6.2.8 => 6.2.8
┆Issue is synchronized with this Asana task by Unito
Issue Analytics
- State:
- Created 2 years ago
- Comments:9 (4 by maintainers)

Top Related StackOverflow Question
Sure!
From some additional testing, it seems like this is only broken with the Webpack 5 builder after all. This is the culprit: https://github.com/mnquintana/storybook-font-preload-repro/blob/storybook%406.2-webpack5-minimal-repro/styles/global.css#L4-L6
Something changed with how paths are resolved in CSS files in the Webpack 5 builder. When I tried using the original paths from Storybook 6.1, Webpack threw this error:
That same path resolves as expected with the Webpack 4 builder on Storybook 6.2.8.
I had to change that path in
global.cssto the relative path on disk for Webpack to resolve the path properly, but then it broke font preloading in production builds because Webpack 5 resolves that path to one that includes a hash in the filename, which can’t be referenced from withinpreview-head.html.I had the same issue.
My Fonts are in src/fonts/ and are included in my stylesheet via scss imports, thus emitted by webpack as an asset. For preloading to work the font definition in the css must use the same url as the <link> tag in the head – using storybooks static file serving isn’t an option in that case.
The default webpack configuration used by storybook generates different filenames depending on the production flag (which is probably when you run storybook dynamically and true when you build it). https://github.com/storybookjs/storybook/blob/64122cb5201b5a0473fdc079726610cf39c04ad4/lib/builder-webpack5/src/preview/base-webpack.config.ts#L54-L62
So if you add one url to your preview-head.html it will only work in one mode and not the other.
The workaround is to simply preload both urls and live with the 404 error
e.g.
Alternative workaround:
Use the webpackFinal option and change the rule mentioned above to use the same filename in both modes.
For a proper fix we would need access to the generated filename in preview-head.html or in the previewHead option.
Or integrate a webpack plugin like https://www.npmjs.com/package/webpack-font-preload-plugin to change the header?