How to Fix Next.js Google Fonts Link Tags Not Working: Server-Side Import Guide

Google Font link tags fail to generate when you import next/font/google inside a client component or omit required configuration options like subsets, so you must always use static imports within server components to enable automatic stylesheet injection.

When implementing nextjs google fonts in modern Next.js applications, missing <link> tags or silent font loading failures typically indicate incorrect usage of the server-side font optimization API. The next/font/google module introduced in Next.js 13 runs exclusively during server-side rendering to inject preconnect hints and stylesheets, but requires strict implementation patterns to function correctly across the App and Pages routers.

The next/font/google loader executes only during SSR to minimize bundle size and optimize loading performance. According to the source code in packages/next/font/google/index.js, the API validates the Next.js version at import time and throws a runtime error if invoked in a client-only bundle. When the loader fails, no <link rel="preload"> or <link rel="stylesheet"> tags reach the HTML head, leaving you with fallback fonts.

Client Component Usage

Importing the font in any file marked with "use client" forces the loader to run in the browser, where it cannot emit head tags. The code in packages/next/font/google/index.js explicitly guards against this scenario, resulting in the "@next/font/google failed to run" error or silent failures.

Missing Required Subsets or Weights

The loader validates requests against the Google Fonts API. Omitting the subsets array triggers the Missing specified subset error documented in errors/google-fonts-missing-subsets.mdx, preventing any CSS generation. Similarly, invalid display values trigger errors per errors/google-font-display.mdx and halt tag injection.

Dynamic Imports in Client Contexts

Using import('next/font/google') or next/dynamic with ssr: false forces the loader into a client-only execution context. Because the font loader must emit tags during the server render, dynamic imports on the client result in missing fonts and broken styling.

How next/font/google Works Under the Hood

When you execute a static import, the code resolves to packages/font/google/loader.js, which re-exports the compiled loader from packages/font/google/dist/google/loader. During the build or server render, this loader:

  1. Contacts the Google Fonts CSS API
  2. Generates a minimal, optimized stylesheet
  3. Creates a unique CSS class name (e.g., inter-var)
  4. Injects the corresponding <link> tags into the HTML head via the Next.js server runtime

The generated class name is then exported for use in your components, but the critical stylesheet injection happens only on the server.

Proper Implementation Methods

App Router Server Components (app/layout.tsx)

Import the font statically in your root layout without the "use client" directive. The loader automatically handles head tag injection.

// app/layout.tsx – server component (no "use client")
import './globals.css';
import { Inter } from 'next/font/google';

const inter = Inter({
  subsets: ['latin'],
  weight: ['400', '700'],
  display: 'swap',
});

export default function RootLayout({
  children,
}: React.PropsWithChildren) {
  return (
    <html lang="en">
      <head />
      <body className={inter.className}>{children}</body>
    </html>
  );
}

Source: packages/create-next-app/templates/app/js/app/layout.js

Pages Router (_app.tsx)

In the Pages Router, the default server-rendering context supports next/font/google without additional configuration.

// pages/_app.tsx – server-rendered (default)
import type { AppProps } from 'next/app';
import { Roboto } from 'next/font/google';

const roboto = Roboto({
  subsets: ['latin'],
  weight: ['400', '500'],
  display: 'optional',
});

export default function MyApp({ Component, pageProps }: AppProps) {
  return (
    <main className={roboto.className}>
      <Component {...pageProps} />
    </main>
  );
}

Antipattern: Client-Only Usage

Never import the font loader in a client component. The following pattern triggers the runtime guard in packages/next/font/google/index.js and prevents link tag generation:

// ❌ This file has "use client" at the top
'use client';
import { Inter } from 'next/font/google'; // ❌ will throw at runtime

If you need font styles inside a client component, compute the class name in a parent server component and pass it as a prop.

Manual Fallback for Edge Cases

When you cannot use next/font/google—such as in external packages or legacy setups—manually inject the Google Fonts CDN links using the Head component from next/head.

// pages/_document.tsx or a custom <Head> component
import Head from 'next/head';

export default function CustomHead() {
  return (
    <Head>
      <link
        rel="preconnect"
        href="https://fonts.gstatic.com"
        crossOrigin="anonymous"
      />
      <link
        href="https://fonts.googleapis.com/css2?family=Krona+One&display=swap"
        rel="stylesheet"
      />
    </Head>
  );
}

Supported display values are swap, fallback, optional, and block, as enforced by the validation logic in errors/google-font-display.mdx.

Summary

  • next/font/google is a server-only API that runs during SSR to inject <link> tags automatically.
  • Always provide required subsets (e.g., ['latin']) and valid display values to prevent loader errors.
  • Import fonts statically in server components (app/layout.tsx or pages/_app.tsx), never in files marked with "use client".
  • Dynamic imports of the font loader force client-side execution and break stylesheet injection.
  • For unsupported scenarios, manually add Google Fonts CDN links using next/head with proper preconnect hints.

Frequently Asked Questions

Can I use next/font/google in a client component?

No. The API is explicitly designed for server-side execution only. According to packages/next/font/google/index.js, importing it in a client component triggers a runtime error because the loader cannot emit <link> tags from the browser. Pass the generated className from a server parent component instead.

Why am I getting a "Missing specified subset" error?

The Google Fonts loader requires at least one subset (such as 'latin') to construct the API request. The error originates from errors/google-fonts-missing-subsets.mdx and prevents tag generation until you add the subsets array to your font configuration object.

Can I dynamically import Google Fonts using next/dynamic?

No. Dynamic imports with ssr: false or runtime import() statements force the loader to execute in the browser, where it lacks access to the HTML head. This results in missing fonts and potentially the "@next/font/google failed to run" error. Use static imports at the top of server components only.

What Next.js version is required for next/font/google?

You need Next.js 13.0.0 or higher. The runtime guard in packages/next/font/google/index.js checks the installed version and throws if it detects versions below 13.0.0. For older versions, use the legacy @next/font package or manual CDN links.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →