Next.js has font optimization option using their package next/font
using google fonts and local. When adding a storybook to a Next.js project, you will start to wonder on how we can achieve the same font to be loaded into the storybook itself as well.
To solve this
We can reuse next/font
that we use in the app/layout.tsx
in our storybook configuration. To do this, we need to create another file and export the font from there to be used in our app/layout.tsx
and our storybook configuration.
// app/font.ts
// Pick your font family that you want
import { Mukta, PT_Serif } from 'next/font/google';
export const fontSerif = PT_Serif({
subsets: ['latin'],
weight: ['400', '700'],
variable: '--font-serif',
display: 'swap'
});
export const fontSans = Mukta({
subsets: ['latin'],
weight: ['300', '400', '500', '700'],
variable: '--font-sans',
display: 'swap'
});
In the .storybook/preview.tsx
, we just need to create a decorator that will inject our variable class using useEffect
so that it is applied in the body, not just in the iframe of our story.
// .storybook/preview.tsx
import { fontSans, fontSerif } from '@/app/font';
import type { Preview } from '@storybook/react';
const preview = {
decorators: [
(Story) => {
useEffect(() => {
document.body.classList.add(
fontSans.variable,
fontSerif.variable,
'font-sans',
'antialiased'
);
}, []);
return (
<TooltipProvider>
<Story />
</TooltipProvider>
);
}
],
} satisfies Preview;
export default preview;
Why do we do this? The reason is if we do add the font class in a div
and have the <Story />
as the children - it will not work for Dialog
/ Modal
component that injects itself in a Portal
as sibling of the storybook iframe - hence our font will not be applied.
Since Shadcn UI
has been mostly a popular option for many frontend developers to be used and their Dialog
component is using Radix UI
which is injecting element into the Portal
as sibling of the iframe.
Now after adding this decorator, you should see the body has the class added as well as the font updated.
Cheers!