Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Bug]: [@storybook/nextjs] Using @next/font breaks storybook v7 #19711

Closed
madeleineostoja opened this issue Nov 2, 2022 · 24 comments
Closed
Assignees

Comments

@madeleineostoja
Copy link

madeleineostoja commented Nov 2, 2022

Describe the bug

Using Next 13's new @next/font font loader, which is required for loading fonts in the new /app routes directory, breaks storybook v7 if it is referenced. Not sure if something can be done to shim it here or if it needs to be fixed upstream.

The workaround is to ensure that no code that touches storybook references the font loader (eg: global styles etc), which could be documented if nothing else.

To Reproduce

No response

System

System:
    OS: macOS 13.0
    CPU: (8) arm64 Apple M1
  Binaries:
    Node: 18.3.0 - /opt/homebrew/bin/node
    Yarn: 1.22.15 - /usr/local/bin/yarn
    npm: 8.11.0 - /opt/homebrew/bin/npm
  Browsers:
    Chrome: 107.0.5304.87
    Firefox: 103.0.2
    Safari: 16.1
  npmPackages:
    @storybook/addon-essentials: future => 7.0.0-alpha.34 
    @storybook/nextjs: ^7.0.0-alpha.41 => 7.0.0-alpha.41

Additional context

Screenshot 2022-11-02 at 4 54 19 PM

@yannbf
Copy link
Member

yannbf commented Nov 2, 2022

Hey @madeleineostoja thanks for opening this issue. This is definitely something we want to get working, but it's really tricky, given how the @next/font component works. I could be wrong, but I think it:

  • Requests the font assets that you need at build time (as you run next dev)
  • Stores them as media in the .next folder
  • Creates a global css which gets injected somehow
  • Provides you a classname which will work as both the assets and css are available

So this process is very Next.js dependent, and it's probably tough to detach. I don't think it's simple to mock it. This exact error happens when running components that use the font import in Jest/Vitest, by the way.

@hanneslund sorry to ping you but I think you're probably the best person to give opinions on how to proceed with this issue. Any thoughts? Would be super nice to be able to use/mock this somehow in external environments. Thank you so much!

@yannbf yannbf added nextjs and removed needs triage labels Nov 2, 2022
@hanneslund
Copy link

Hi! Is there an example somewhere I can clone and take a look? Thanks!

which is required for loading fonts in the new /app routes directory

Just to clarify, it's not required but it gives you a bunch of stuff automatically. You can still add @font-faces in your CSS as usual.

@yannbf
Copy link
Member

yannbf commented Nov 4, 2022

Hi! Is there an example somewhere I can clone and take a look? Thanks!

Thanks for the swift reply @hanneslund! I set up an example with instructions here for you:
https://github.com/yannbf/nextjs-font-issue

Happy to discuss further between our teams and come up with a good solution together!

@robcaldecott
Copy link

I am hitting this issue (using @next/font/local) and I can't even mock it out with a webpack alias which is odd. If I could get that to work then I could just import the font as CSS in preview.js so my Stories look OK.

main.js

webpackFinal: (config) => {
  // Does not seem to work
  config.resolve.alias["@next/font/local"] = require.resolve("./my-font-mock.js");
  return config;
})

my-font-mock.js

function localFont() {
  // This is never called
  return { style: { fontFamily: "Arial" } };
}

export default localFont;

preview.js

import { theme } from "../src/theme";

theme.js

import localFont from "@next/font/local";
import { createTheme } from "@mui/material";

cont myFont = localFont({ src: "../public/font.woff2" });

return createTheme({
  typography: {
      fontFamily: myFont.style.fontFamily,
    },
});

@hanneslund
Copy link

@next/font is now mocked in next/jest.

I also got this to work to mock @next/font/google in a story.

  webpackFinal: async (config) => {
    config.resolve.alias = {
      ...config.resolve.alias,
      "@next/font/google": require.resolve("./nextFontGoogle"),
    }

    return config;
}

This will set the class in .className to be named the same as the function imported from @next/font/google, e.g. Playfair_Display.

// nextFontGoogle.js
module.exports = new Proxy(
    {},
    {
      get: function getter(_, receiver) {
        return () => ({
          className: receiver,
        })
      },
    }
  )
  

And then import a CSS files that defines the font inside the story.

@font-face {
  font-family: "Playfair_Display";
  font-style: normal;
  font-weight: 400 900;
  font-display: swap;
  src: url(https://fonts.gstatic.com/s/playfairdisplay/v30/nuFiD-vYSZviVYUb_rj3ij__anPXDTzYgEM86xQ.woff2)
    format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215,
    U+FEFF, U+FFFD;
}

.Playfair_Display {
  font-family: "Playfair_Display", serif;
}

@robcaldecott
Copy link

My mock is not working because of lazyCompilation in main.js:

core: {
    builder: {
      name: "webpack5",
      options: {
        lazyCompilation: true,
        fsCache: true,
      },
    },
  },

Removing lazyCompilation allows me to mock out @next/font/local but I lose the performance benefits that lazyCompilation brings.

@valentinpalkovic valentinpalkovic self-assigned this Dec 6, 2022
@hobadams
Copy link

Hey folks, any update on this issue? I'm getting the same thing in storybook Uncaught Error: @next/font/local failed to run or is incorrectly configured.

@valentinpalkovic
Copy link
Contributor

valentinpalkovic commented Dec 15, 2022

Hi @hobadams.

I am working on a fully-fledged solution right now. Due to the upcoming holiday season, the release of @next/font support might occur at the year's end or in the first weeks of January. Maybe I can already release something tomorrow that doesn't break Storybook completely.

@shilman shilman moved this to Required for RC in Core Team Projects Dec 15, 2022
@shilman
Copy link
Member

shilman commented Dec 20, 2022

Crikey!! I just released https://github.com/storybookjs/storybook/releases/tag/v7.0.0-beta.13 containing PR #20291 that references this issue. Upgrade today to the @next NPM tag to try it out!

npx sb upgrade --prerelease

Closing this issue. Please re-open if you think there's still more to do.

@shilman shilman closed this as completed Dec 20, 2022
Repository owner moved this from Required for RC to Done in Core Team Projects Dec 20, 2022
@salchichongallo
Copy link

By decoupling the fonts from the theme I was able to workaround the error. This might be useful if you want to use a stable version of Storybook.

In Next.js _app.tsx:

import { Work_Sans } from '@next/font/google';

const workSans = Work_Sans(...); // The point is to execute next/font away from Storybook

const appTheme = createAppTheme({
  fontFamily: {
    heading: workSans.style.fontFamily,
  },
});

Load the font in Storybook somehow, for instance in your preview-head.html:

<link href="https://fonts.googleapis.com/css2?family=Work+Sans:wght@400&display=swap" rel="stylesheet" />

Now we can inject the theme in Storybook's preview.js:

const appTheme = createAppTheme({
  fontFamily: {
    heading: "'Work Sans', sans-serif",
  },
});

export const decorators = [
  Story => (
    <ThemeProvider theme={appTheme}>
      <Story />
    </ThemeProvider>
  ),
];

@valentinpalkovic
Copy link
Contributor

valentinpalkovic commented Dec 22, 2022

Hi @salchichongallo,

this workaround shouldn‘t be necessary anymore. Have you tried the latest prerelease version?

Also make sure to use the @storybook/next framework, like described here: https://storybook.js.org/blog/integrate-nextjs-and-storybook-automatically/

@dalechyn
Copy link

dalechyn commented Feb 10, 2023

Hi @valentinpalkovic, I'm on ^7.0.0-beta.45 and can confirm that @nextjs/font is not picked up by storybook.

@valentinpalkovic
Copy link
Contributor

Hi @h0tw4t3r, if you still have any issues, could you please provide a reproduction?

@dalechyn
Copy link

@valentinpalkovic Sure!

https://storybook.spilnota.xyz/
https://github.com/spilnotaxyz/website

Currently, I bypass this issue by hardcoding the fonts for h3 and the rest:
https://github.com/spilnotaxyz/website/blob/84c591e2a698891e9ec18dd87e5a0611ea34ec69/.storybook/preview.tsx#L27-L39

Another issue that I see is that in storybook dev sometimes the preview-head.html is not picked up during hot rerenders which also makes the fonts go down in dev environment.

@valentinpalkovic
Copy link
Contributor

valentinpalkovic commented Feb 10, 2023

@h0tw4t3r One thing I can identify is that you haven’t set up staticDirs correctly to make your local next/fonts working. Please take a look here: https://github.com/storybookjs/storybook/tree/next/code/frameworks/nextjs#nextfontlocal

@valentinpalkovic
Copy link
Contributor

Second: could you please change the content of lib/fonts.ts to:

import { Inter_Tight } from '@next/font/google'
import localFont from '@next/font/local'

const inter = Inter_Tight({
  weight: ['400'],
  subsets: ['latin'],
  fallback: ['sans-serif']
})

const neueMachina = localFont({
  src: [
    {
      path: '../public/fonts/neue-machina/NeueMachina-Regular.woff2',
      weight: '400',
      style: 'normal'
    },
    {
      path: '../public/fonts/neue-machina/NeueMachina-Bold.woff2',
      weight: '700',
      style: 'normal'
    }
  ]
})

export { infer, neueMachina }

Please let me know, whether the two adjustments fixes it for you

@dalechyn
Copy link

@valentinpalkovic Thanks for the help! Local font import is now resolved.

My project folder structure is next:

lib
├── createEmotionCache.ts
├── fonts.ts
├── index.ts
└── theme.ts
public
├── favicon.ico
└── fonts
    └── neue-machina
        ├── NeueMachina-Black.woff2
        ├── NeueMachina-Bold.woff2
        ├── NeueMachina-Light.woff2
        ├── NeueMachina-Medium.woff2
        ├── NeueMachina-Regular.woff2
        ├── NeueMachina-Ultrabold.woff2
        ├── NeueMachina-Ultralight.woff2
        └── NeueMachina.css

Considering this, I had to correctly set up the staticDirs configuration.

Before:

// .storybook/main.ts

export default {
  // ...
  staticDirs: ['../public']
}

After:

// .storybook/main.ts
export default {
  // ...
  staticDirs: [
    {
      from: '../public',
      to: 'public'
    }
  ]

So I could import the fonts like this:

// lib/fonts.ts
import { Inter_Tight } from '@next/font/google'
import localFont from '@next/font/local'

export const inter = Inter_Tight({
  weight: ['400'],
  subsets: ['latin'],
  fallback: ['sans-serif']
})

export const neueMachina = localFont({
  src: [
    {
      path: '../public/fonts/neue-machina/NeueMachina-Regular.woff2',
      weight: '400',
      style: 'normal'
    },
    {
      path: '../public/fonts/neue-machina/NeueMachina-Bold.woff2',
      weight: '700',
      style: 'normal'
    }
  ]
})

Hope this helps out someone else!

Absolutely love this nextjs extension, great job guys!

dalechyn added a commit to spilnotaxyz/website that referenced this issue Feb 10, 2023
dalechyn pushed a commit to spilnotaxyz/website that referenced this issue Feb 10, 2023
## [1.5.4](v1.5.3...v1.5.4) (2023-02-10)

### Bug Fixes

* **storybook:** 🩹 hotfix introduced by 84c591e => good fix ([e41670b](e41670b)), closes [/github.com/storybookjs/storybook/issues/19711#issuecomment-1425887280](https://github.com//github.com/storybookjs/storybook/issues/19711/issues/issuecomment-1425887280)
@seanhakbb
Copy link

I'm having some strange issues. I feel like I am nearly there.

Here is my setup:
My preview.tsx

const fontIcon = localFont({
	src: [
		{
			path: '../fonts/ssg.woff2',
			weight: '400',
			style: 'normal',
		},
	],
	variable: '--font-ssg',
});

My main.ts static dirs

staticDirs: [
  { from: '../public', to: 'public' },
  { from: '../fonts', to: 'fonts' },
],

I dont get any 404 errors, however it seems my class seems to get broken url reference:

<style id="font-face-font-d8112b">@font-face {
          font-family: font-d8112b;
          src: url(..ontsssg.woff2);
          font-weight: 400;
          font-style: normal;
        }</style>

it removes slash and removes the letter "f". If I manually change this style tag (that is generated in head) I get the font icon loading. (correct path should be /fonts/ssg.woff2)

This font loads fine in my nextjs project.
I have these deps:

"next": "^13.1.5",
"@next/font": "^13.1.6",
"storybook": "^7.0.0-beta.48",
"@storybook/nextjs": "^7.0.0-beta.48"
...

@valentinpalkovic
Copy link
Contributor

@seanhakbb Can you please create a reproduction and provide me a link to it? Then I will be able to debug it if you want :)

@seanhakbb
Copy link

seanhakbb commented Feb 20, 2023

@seanhakbb Can you please create a reproduction and provide me a link to it? Then I will be able to debug it if you want :)

https://github.com/seanhakbb/storybookdebug I set this quick demo up that shows it working with npm run dev, but not in npm run storybook.
(check in the iframe after a class called something like "font-face-font-98bc83")

Im running on Node v18.13.0 btw. Thanks for the help! @valentinpalkovic

@AdrianFahrbach
Copy link

I tried this for a while now and I somehow just can't get it to work on my NX setup. I always either get this error when starting up Storybook:

Failed to load static files, no such directory: /some/path/fonts

Or this error on the Storybook web page:

index.js:15 Uncaught Error: @next/font/local failed to run or is incorrectly configured.
If you just installed `@next/font`, please try restarting `next dev` and resaving your file.

My workaround is to serve the files at / and then loading them with regular @font-face css. We are only using Storybook locally, so this setup is totally fine. You can continue reading if you want to dig into this though.


My setup basically looks like this.
I got a StorybookWrapper.tsx component for the that gets imported in the preview.tsx:

apps/website/.storybook/StorybookWrapper.tsx:

import { interFont, sculpinFont } from "@my-project/components/containers/Global";
import { theme as baseTheme } from "@my-project/styles/theme";
import React from "react";
import { ThemeProvider } from "styled-components";

const theme = {
  ...baseTheme,
  fontFamily: {
    headlines: sculpinFont.style.fontFamily,
    body: interFont.style.fontFamily,
  },
};

const StorybookWrapper: React.FC<{ children: React.ReactNode }> = ({ children }) => {
  return <ThemeProvider theme={theme}>{children}</ThemeProvider>;
};

export default StorybookWrapper;

apps/website/.storybook/preview.tsx:

import StorybookWrapper from "./StorybookWrapper";

export const decorators = [
  (Story) => {
    return <StorybookWrapper>{Story}</StorybookWrapper>;
  },
];

Fonts get created in a Global.tsx.
libs/components/src/lib/containers/Global.tsx:

import localFont from "@next/font/local";

export const interFont = localFont({
  src: [
    {
      path: "../../assets/fonts/inter-v12-latin-regular.woff2",
      weight: "400",
    },
  ],
  fallback: ["Arial", "sans-serif"],
  display: "swap",
});

export const sculpinFont = localFont({
  src: [
    {
      path: "../../assets/fonts/sculpin-700.woff2",
      weight: "400",
    },
  ],
  fallback: ["Arial", "sans-serif"],
  display: "swap",
});

My file structure looks like this:

.storybook/
├─ main.ts
apps/
├─ website/
│  ├─ .storybook/
│  │  ├─ main.ts
│  │  ├─ preview.tsx
│  │  ├─ StorybookWrapper.tsx
libs/
├─ components/
│  ├─ src/
│  │  ├─ lib/
│  │  │  ├─ containers/
│  │  │  │  ├─ Global.tsx
│  │  ├─ assets/
│  │  │  ├─ fonts/

In my main.ts I therefore got this:
apps/website/.storybook/main.ts:

staticDirs: [
    {
      from: '../../../libs/components/src/assets/fonts/',
      to: 'libs/components/src/assets/fonts/',
    },
  ],

From is from the storybook directory and I guess in this case its the nested storybook directory because everything else than the value above throws an error.
The to value should be from the root of my project so the one above should be fine as well (and does not throw an error). ../../../../../../../libs/components/src/assets/fonts/ is working as well, other paths throw an error.

I'm not quite sure where the problem is. It's either that I don't really get how to work with the '../../' in my local font, that my custom ts paths are not working correctly or that NX is in the way.

@yannbf
Copy link
Member

yannbf commented Apr 21, 2023

Hey there @seanhakbb thank you for providing a repro! If you upgrade to the latest Storybook 7 release you will notice that it works correctly:

image

@AdrianFahrbach thanks for providing all this info, would you mind upgrading to the latest Storybook 7 and checking whether you still experience the issues?

@SalahAdDin
Copy link

@valentinpalkovic Thanks for the help! Local font import is now resolved.

My project folder structure is next:

lib
├── createEmotionCache.ts
├── fonts.ts
├── index.ts
└── theme.ts
public
├── favicon.ico
└── fonts
    └── neue-machina
        ├── NeueMachina-Black.woff2
        ├── NeueMachina-Bold.woff2
        ├── NeueMachina-Light.woff2
        ├── NeueMachina-Medium.woff2
        ├── NeueMachina-Regular.woff2
        ├── NeueMachina-Ultrabold.woff2
        ├── NeueMachina-Ultralight.woff2
        └── NeueMachina.css

Considering this, I had to correctly set up the staticDirs configuration.

Before:

// .storybook/main.ts

export default {
  // ...
  staticDirs: ['../public']
}

After:

// .storybook/main.ts
export default {
  // ...
  staticDirs: [
    {
      from: '../public',
      to: 'public'
    }
  ]

So I could import the fonts like this:

// lib/fonts.ts
import { Inter_Tight } from '@next/font/google'
import localFont from '@next/font/local'

export const inter = Inter_Tight({
  weight: ['400'],
  subsets: ['latin'],
  fallback: ['sans-serif']
})

export const neueMachina = localFont({
  src: [
    {
      path: '../public/fonts/neue-machina/NeueMachina-Regular.woff2',
      weight: '400',
      style: 'normal'
    },
    {
      path: '../public/fonts/neue-machina/NeueMachina-Bold.woff2',
      weight: '700',
      style: 'normal'
    }
  ]
})

Hope this helps out someone else!

love this nextjs extension, great job guys!

Worked for me.

@SalahAdDin
Copy link

But it creates a conflict with msw-storybook-addon: It always looks for the mock service file on the root of the URL, when using custom redirects, that file is served in the given folder name, so, the addon is not able to find it; while, when serving the public folders are served on "public" URL instead to being served in the root URL.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Archived in project
Development

No branches or pull requests