Replies: 15 comments 16 replies
-
Did you figure out a solution for dynamic font loading? Anything with Nextjs's new @next/font library? https://nextjs.org/docs/basic-features/font-optimization |
Beta Was this translation helpful? Give feedback.
-
Either of you figure this one out? I managed to get a little something going. It's not perfect. I have the same use case as @nicosh - Platforms Starter Kit, user themes, custom fonts for each theme. I'm on /app dir though. Basically involves dynamically importing the module with the next/font definitions into page.tsx, then I made a client component to load the fonts on the client. The result is I get just the fonts I need, but the downside is there's a little FOUT on first load. |
Beta Was this translation helpful? Give feedback.
-
I am also having the same problem in my platform of a multi-tenant app - https://letterpad.app Note: I am using nextJs 14 with the After understanding how these fonts work and trying multiple different ways, I came to realise that its not possible. Below are my findings: To use a font, you declare it like this: import { Inter } from 'next/font/google';
export const inter = Inter({
subsets: ['latin'],
display: 'swap',
}); When the build runs, it is going to recognise this But when you are using variable font, you will receive the font name from a database call or something. These were my attempts: Attempt 1:import { Inter , Lora } from 'next/font/google';
const fonts = {
Inter: Inter({
subsets: ['latin'],
display: 'swap',
}),
Lora: Lora({
subsets: ['latin'],
display: 'swap',
}),
}
const Layout = () => {
const theme = await getTheme();
const font = fonts[theme.font].className;
return ...
} The above code is not going to work because the build is not going to recognise the font. It is not used at the root but instead inside an object Attempt 2I create two files - // Inter.tsx
const inter = Inter({
subsets: ['latin'],
display: 'swap',
})
export default function({children}) {
return <div className={inter.className}>{children}</div>
} In layout I use it like this: import dynamic from "next/dynamic";
const inter = dynamic(() => import('./inter'));
const lora = dynamic(() => import('./lora'));
const Layout = () => {
const theme = await getTheme();
const FontWrapper = theme.font === "inter" ? inter : lora
return <FontWrapper>... layout stuff</FontWrapper>
} This is going to work if this is server rendered but it is going preload both the fonts. Attempt 3Next attempt was to load it in client but also use preload. However, next does not allow preloading of lazy components in client. I tried using Attmept 4Next attempt was to use const inter = dynamic(() => import('./fonts/inter'));
const lora = dynamic(() => import('./fonts/inter'));
const Layout = () => {
const theme = await getTheme();
const font = await import(`./fonts/${theme.fontName}`);
return <div className={font.className}>... layout stuff</div>
} When you do dynamic path import, webpack will try to build all the files inside the Attempt 5This attempt to provide with /* webpackChunkName: "primaryFont" */ with dynamic import. Knowing this chunk name will allow me to manually add the preload script. However, in network tab, I was unable to see the chunk with the name ConclusionThere is no clean solution to this yet. |
Beta Was this translation helpful? Give feedback.
-
I'm having the same problem. |
Beta Was this translation helpful? Give feedback.
-
My solution of same problem (draft implementation): |
Beta Was this translation helpful? Give feedback.
-
A follow up - here's a Stackblitz for a basic version of how I'm dynamically loading fonts. Swap the fonts being used by changing the Note the network tab to see that only the selected fonts are loading. |
Beta Was this translation helpful? Give feedback.
-
We're loading specific fonts based on given locale, and would love to conditionally load one of the four fonts we have available. Currently it preloads them all, and only conditionally selects one of them, based on the className we set. |
Beta Was this translation helpful? Give feedback.
-
Any solution? |
Beta Was this translation helpful? Give feedback.
-
I have a multi-tenant app, each tenant has its own theme, colors, and fonts. and fonts are loaded from Google Fonts or my own hosted fonts based on the tenant choice so here is how I am doing it: import React from "react";
import { notFound } from "next/navigation";
import { fetchTenant } from "@/app/lib/fetchers";
import "@/styles/globals.scss";
const DEFAULT_FONTS = ["Dubai", "Cairo", "Sahel", "Samim", "Speda", "Vazir", "JFFlat"];
const DEFAULT_FONT = "Dubai";
type RootLayoutProps = {
children: React.ReactNode;
params: {
locale: string;
[key: string]: string;
};
};
export default async function RootLayout({ children }: RootLayoutProps) {
const tenant = await fetchTenant();
if (!tenant) {
return notFound();
}
const fontFamily = tenant.font_family ?? DEFAULT_FONT;
const fontBody = tenant.font_body ?? DEFAULT_FONT;
const dir = tenant.locale === "ar" ? "rtl" : "ltr";
return (
<html
dir={dir}
lang={tenant.locale}
>
<head>
{DEFAULT_FONTS.includes(fontFamily) ? (
<style>{`@import url(https://self-hosted.com/assets/fonts/${fontFamily}/style.css);`}</style>
) : (
<style>{`@import url(https://fonts.googleapis.com/css2?family=${fontFamily.replaceAll(" ", "+")});`}</style>
)}
{fontBody !== fontFamily &&
(DEFAULT_FONTS.includes(fontBody) ? (
<style>{`@import url(https://self-hosted.com/assets/fonts/${fontBody}/style.css);`}</style>
) : (
<style>{`@import url(https://fonts.googleapis.com/css2?family=${fontBody.replaceAll(" ", "+")});`}</style>
))}
<style
dangerouslySetInnerHTML={{
__html: `
:root {
--bs-font-sans-serif: "${fontFamily}", system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial,
"Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
--headlines-font-family: var(--bs-font-sans-serif);
--paragraph-font-family: "${fontBody}", system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue",
Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
--bs-body-font-family: "${fontBody}", system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial,
"Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
}
body {
font-family: var(--paragraph-font-family);
}
h1,
h2,
h3,
h4,
h5,
h6 {
color: var(--bs-headline) !important;
font-family: var(--headlines-font-family) !important;
}
`
}}
/>
</head>
{children}
</html>
);
} |
Beta Was this translation helpful? Give feedback.
-
any updates on this? |
Beta Was this translation helpful? Give feedback.
-
I understand that the font loader files must specify literals currently as you get a "Font loader values must be explicitly written literals" error when you try and add in env variables. But why is this, why can't an env file be used to dynamically specify values at build time? e.g.
|
Beta Was this translation helpful? Give feedback.
-
Beta Was this translation helpful? Give feedback.
-
I've got a flashing of the default font when I open the MUI modal with the manual adding the
Then render the |
Beta Was this translation helpful? Give feedback.
-
✅ Dynamic Google Fonts in Next.js 15 App Router (Clean Solution)I ran into this exact problem while building a tool Because the detected font could change dynamically after a user uploads an image, I needed a way to load fonts at runtime, inside the App Router, without predefining them in The Challenge: ✅ The Solution:
const response = await fetch(`https://fonts.googleapis.com/css2?family=${encodeURIComponent(fontName)}:wght@100;200;300;400;500;600;700;800;900&display=swap`)
const css = await response.text()
const [result, setResult] = useState<ApiResponse | null>(null)
const fontStyleRef = useRef<HTMLStyleElement | null>(null)
useEffect(() => {
if (result?.css) {
// Remove any previously injected font
if (fontStyleRef.current) {
fontStyleRef.current.remove()
fontStyleRef.current = null
}
// Inject new font CSS
const style = document.createElement("style")
style.textContent = result.css
document.head.appendChild(style)
fontStyleRef.current = style
}
}, [result])
Why this works cleanly:
I tested a lot of other approaches (dynamic import hacks, manual It’s been production-tested and works extremely well. 🧹 Minor optional notes you could add:
|
Beta Was this translation helpful? Give feedback.
-
✅ Dynamic Google Fonts with Next.js 15 (Client Component Approach)I recently encountered the same issue and wanted to share a fully working dynamic solution for loading Google Fonts at runtime using Next.js 15 with Tailwind CSS v4. This is particularly useful when the font name comes from an external source (like an API or user preferences). 🔧 Solution OverviewThis approach uses:
📦 Code Breakdown1.
|
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
Hi,
We have a multi tenancy app (using platforms starter kit) that will render different websites, each of them can use different fonts.
Lets say we have a total o 50 different fonts avaible (from google fonts), each website may use only one font just as it could use all of them, which is the best solution to ensure the best performance ?
<Head>
(ignoring No Stylesheets In Head Component) and detect which font load usinggetStaticProps
I've not mentionated to use custom
_document
, since it does not supportgetStaticProps
while the app eavily depends on on demand revalidation.Thanks
Beta Was this translation helpful? Give feedback.
All reactions