How to dynamically import fonts?
Unanswered
American black bear posted this in #help-forum
American black bearOP
Hello, I have a CMS system setup where the user can select a predefined set of fonts. On my root
layout.tsx
I fetch the font name with custom function getFont()
which returns a font key
for example: "inter"
, "playfair"
, etc. Is it possible to somehow dynamically import only the font selected by user and use it in my app without hurting the performance by importing a buch of predefined ones?4 Replies
@American black bear Hello, I have a CMS system setup where the user can select a predefined set of fonts. On my root `layout.tsx` I fetch the font name with custom function `getFont()` which returns a font `key` for example: `"inter"`, `"playfair"`, etc. Is it possible to somehow dynamically import only the font selected by user and use it in my app without hurting the performance by importing a buch of predefined ones?
next/font
is certainly out of the question here. i also faced this problem before and i just used the google fonts api (iirc) to get the font css url and then use a <link>
in your root layout to use that urlyou can use other apis as well, for example fontsource or any method you know of to retrieve the css file url
you can also use cloudflare fonts if you use cloudflare; in that case you may find this snippet helpful
American black bearOP
so for example like this:
// lib/fonts.ts
export function getFontUrl(fontName: string): string {
const fontMap: { [key: string]: string } = {
inter: 'Inter:wght@400;700',
playfair: 'Playfair+Display:wght@400;700',
roboto: 'Roboto:wght@400;500',
};
const formattedFont = fontMap[fontName] || fontMap.inter;
return `https://fonts.googleapis.com/css2?family=${formattedFont}&display=swap`;
}
// app/layout.tsx
export default async function RootLayout({
children,
}: Readonly<{ children: React.ReactNode }>) {
const fontUrl = getFontUrl("inter")
return (
<html
lang="en"
suppressHydrationWarning
>
<head>
<script
dangerouslySetInnerHTML={{
__html: `
<link href="${fontUrl}" rel="stylesheet" />
);
}
@American black bear so for example like this:
ts
// lib/fonts.ts
export function getFontUrl(fontName: string): string {
const fontMap: { [key: string]: string } = {
inter: 'Inter:wght@400;700',
playfair: 'Playfair+Display:wght@400;700',
roboto: 'Roboto:wght@400;500',
};
const formattedFont = fontMap[fontName] || fontMap.inter;
return `https://fonts.googleapis.com/css2?family=${formattedFont}&display=swap`;
}
tsx
// app/layout.tsx
export default async function RootLayout({
children,
}: Readonly<{ children: React.ReactNode }>) {
const fontUrl = getFontUrl("inter")
return (
<html
lang="en"
suppressHydrationWarning
>
<head>
<script
dangerouslySetInnerHTML={{
__html: `
<link href="${fontUrl}" rel="stylesheet" />
`,
}}
/>
</head>
<body>
{children}
</body>
</html>
);
}
if you dont use cloudflare fonts or a proxy that changes the
but yes basically you get the css url from somewhere and render it
<link>
tag without nextjs knowing, you don't need to use the <script>
trick, you can just <link href={...} />
directlybut yes basically you get the css url from somewhere and render it
I had that issue before too, the way I solved it was to dynamically changing the href pointing to google fonts API, in a link tag
I had a dedicated component to handle this, based on a global state that notified me when fonts changed.
Here's the full snippet.
Btw, browsers cache the previously loaded fonts by default, so this doesn't make additional requests for fonts that were already loaded.
<link href={sansFontHref} rel="stylesheet" precedence="high" />
I had a dedicated component to handle this, based on a global state that notified me when fonts changed.
Here's the full snippet.
export function FontLoader() {
const { currentFonts } = useThemeConfig();
// Do whatever, you just need the font href
const sansFontHref = [
...sansFontsArray,
...serifFontsArray,
...monoFontsArray,
].find((font) => font.value === currentFonts?.sans)?.href;
const serifFontHref = serifFontsArray.find(
(font) => font.value === currentFonts?.serif,
)?.href;
const monoFontHref = monoFontsArray.find(
(font) => font.value === currentFonts?.mono,
)?.href;
return (
<>
{sansFontHref && (
<link href={sansFontHref} rel="stylesheet" precedence="high" />
)}
{serifFontHref && (
<link href={serifFontHref} rel="stylesheet" precedence="high" />
)}
{monoFontHref && (
<link href={monoFontHref} rel="stylesheet" precedence="high" />
)}
</>
);
}
Btw, browsers cache the previously loaded fonts by default, so this doesn't make additional requests for fonts that were already loaded.