Falytom logoFALYTOM
Games
ToolsUI ComponentsServicesProductsAbout
menu
  1. Home
  2. /Web Development
  3. /Nextjs
  4. /Font Optimization
Your Progress0%
0 of 70 completed

NextJS Topics

Getting Started

  • What is Next.js?
  • Next.js vs React - Key Differences
  • Creating Your First Next.js 15 Project
  • Next.js Project Structure Explained
  • App Router vs Pages Router

Routing Fundamentals

  • File-Based Routing Basics
  • Creating Pages with page.tsx
  • Dynamic Routes and Route Parameters
  • Catch-All and Optional Catch-All Routes
  • Route Groups for Organization
  • Parallel Routes
  • Intercepting Routes

Layouts and Pages

  • Understanding Layouts
  • Root Layout and Global Configuration
  • Nested Layouts
  • Templates vs Layouts
  • Loading States with loading.tsx
  • Error Handling with error.tsx

Server and Client Components

  • Understanding Server Components
  • Client Components with 'use client'
  • When to Use Server vs Client Components
  • Component Composition Patterns
  • Passing Props Between Server and Client
  • Server Component Patterns and Best Practices

Data Fetching

  • Fetching Data in Server Components
  • Parallel and Sequential Data Fetching
  • Data Fetching Patterns and Strategies
  • Caching and Revalidation
  • Revalidate and Cache Tags
  • Handling Loading and Error States in Data Fetching

Navigation and Links

  • Link Component Basics
  • useRouter Hook for Programmatic Navigation
  • usePathname and useSearchParams Hooks
  • Active Links and Navigation States
  • Redirects and Navigation Guards

Styling in Next.js

  • CSS Modules in Next.js
  • Tailwind CSS Setup and Configuration
  • Global Styles and CSS Variables
  • Next.js Font Optimization

Images and Media

  • Next.js Image Component Basics
  • Image Optimization and Best Practices
  • Working with Static Assets

Forms and Data Mutations

  • Understanding Server Actions
  • Form Handling with Server Actions
  • Form Validation and Error Handling
  • useFormStatus and useFormState Hooks
  • Revalidating Data After Mutations
  • Optimistic Updates

Metadata and SEO

  • Static Metadata Configuration
  • Dynamic Metadata Generation
  • Open Graph and Social Media Cards
  • Sitemap and Robots.txt

API Routes and Route Handlers

  • Route Handlers Introduction
  • GET and POST Request Handlers
  • Dynamic API Routes
  • Request and Response Objects
  • API Error Handling and Status Codes

Middleware and Advanced Features

  • Introduction to Middleware
  • Authentication with Middleware
  • Environment Variables and Configuration
  • Streaming and Suspense
  • Not Found and Global Error Pages

Deployment and Optimization

  • Understanding Static and Dynamic Rendering
  • generateStaticParams for Static Generation
  • Build and Production Optimization
  • Deploying to Vercel
  • Alternative Deployment Options
  • Performance Monitoring and Analytics

Real-World Projects

  • Project 1: E-commerce Product Catalog
  • Project 2: Dashboard with Authentication

Next.js Font Optimization

Automatic font optimization with next/font

Web fonts are essential for beautiful typography, but they come with performance costs— external network requests, slow loading, and dreaded layout shift when fonts load. Next.js solves this with next/font, a powerful system that automatically optimizes fonts, self-hosts them (no external requests), and uses CSS size-adjust to eliminate layout shift completely. Let's master font optimization!

Why next/font?

❌ Traditional Web Fonts

HTML
<!-- Traditional approach -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&display=swap" rel="stylesheet">

<!-- Problems: -->
<!-- ❌ External network request (slow) -->
<!-- ❌ Privacy concerns (Google tracks) -->
<!-- ❌ Layout shift when font loads -->
<!-- ❌ No optimization -->
<!-- ❌ Depends on external service -->

✅ next/font

TYPESCRIPT
// Modern approach with next/font
import { Inter } from 'next/font/google';

const inter = Inter({ subsets: ['latin'] });

// Benefits:
// ✅ Automatic optimization at build time
// ✅ Self-hosted (no external requests)
// ✅ Zero layout shift
// ✅ Privacy-friendly
// ✅ Smaller bundle size
// ✅ Better performance

Performance Benefits

  • No external requests: Fonts self-hosted at build time
  • Zero layout shift: CSS size-adjust prevents text reflow
  • Optimized delivery: Only loads characters you use
  • Automatic preloading: Critical fonts preloaded automatically
  • Privacy-friendly: No tracking from external font services

Using Google Fonts

Basic Google Font Setup

app/layout.tsx
import { Inter } from 'next/font/google';

// Configure font
const inter = Inter({
  subsets: ['latin'],
  display: 'swap',
});

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

// ✅ Font applied to entire app
// ✅ Optimized automatically
// ✅ No layout shift

Multiple Font Weights

app/layout.tsx
import { Inter } from 'next/font/google';

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

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

// Usage in CSS:
// font-weight: 400; → Regular
// font-weight: 600; → Semibold
// font-weight: 700; → Bold

// ✅ Loads only specified weights
// ✅ Smaller bundle than loading all weights

Variable Fonts (Recommended)

app/layout.tsx
import { Inter } from 'next/font/google';

const inter = Inter({
  subsets: ['latin'],
  // Variable font - includes all weights in one file
  // More efficient than multiple weight files
  variable: '--font-inter', // Creates CSS variable
});

export default function RootLayout({ children }) {
  return (
    <html lang="en" className={inter.variable}>
      <body>{children}</body>
    </html>
  );
}

// ✅ Variable font = all weights in one file
// ✅ More efficient than multiple files
// ✅ Smooth weight transitions
// ✅ Creates --font-inter CSS variable

Using Font CSS Variables

app/globals.css
/* Font variable automatically available */
body {
  font-family: var(--font-inter), system-ui, sans-serif;
}

.heading {
  font-family: var(--font-inter);
  font-weight: 700;
}

.body-text {
  font-family: var(--font-inter);
  font-weight: 400;
}

// ✅ Use CSS variable throughout styles
// ✅ Easy to change font later
// ✅ Works with CSS Modules and Tailwind

Multiple Google Fonts

app/layout.tsx
import { Inter, Playfair_Display } from 'next/font/google';

// Sans-serif for body
const inter = Inter({
  subsets: ['latin'],
  variable: '--font-sans',
});

// Serif for headings
const playfair = Playfair_Display({
  subsets: ['latin'],
  variable: '--font-serif',
});

export default function RootLayout({ children }) {
  return (
    <html lang="en" className={`${inter.variable} ${playfair.variable}`}>
      <body>{children}</body>
    </html>
  );
}

// Usage in CSS:
// body { font-family: var(--font-sans); }
// h1, h2, h3 { font-family: var(--font-serif); }

// ✅ Multiple fonts with CSS variables
// ✅ Each optimized separately
// ✅ Easy to use throughout app
app/globals.css
body {
  font-family: var(--font-sans), system-ui, sans-serif;
}

h1, h2, h3, h4, h5, h6 {
  font-family: var(--font-serif), Georgia, serif;
  font-weight: 700;
}

.code {
  font-family: 'Courier New', monospace;
}

// ✅ Sans for body text
// ✅ Serif for headings
// ✅ Clear typography hierarchy

Using Local Fonts

Loading Local Font Files

app/layout.tsx
import localFont from 'next/font/local';

const myFont = localFont({
  src: './fonts/MyFont.woff2',
  display: 'swap',
  variable: '--font-custom',
});

export default function RootLayout({ children }) {
  return (
    <html lang="en" className={myFont.variable}>
      <body>{children}</body>
    </html>
  );
}

// ✅ Load custom fonts from your project
// ✅ Same optimization as Google Fonts
// ✅ Full control over font files

Multiple Font Files (Different Weights)

app/layout.tsx
import localFont from 'next/font/local';

const myFont = localFont({
  src: [
    {
      path: './fonts/MyFont-Regular.woff2',
      weight: '400',
      style: 'normal',
    },
    {
      path: './fonts/MyFont-Italic.woff2',
      weight: '400',
      style: 'italic',
    },
    {
      path: './fonts/MyFont-Bold.woff2',
      weight: '700',
      style: 'normal',
    },
  ],
  variable: '--font-custom',
});

export default function RootLayout({ children }) {
  return (
    <html lang="en" className={myFont.variable}>
      <body>{children}</body>
    </html>
  );
}

// ✅ Multiple weight/style variants
// ✅ Browser picks correct file based on CSS
// ✅ Optimized loading

Variable Local Font

app/layout.tsx
import localFont from 'next/font/local';

const geist = localFont({
  src: './fonts/GeistVF.woff2',
  variable: '--font-geist',
  weight: '100 900', // Variable font weight range
});

export default function RootLayout({ children }) {
  return (
    <html lang="en" className={geist.variable}>
      <body>{children}</body>
    </html>
  );
}

// ✅ Variable font with full weight range
// ✅ One file, all weights
// ✅ Smooth transitions between weights

Integrating with Tailwind CSS

Configure Tailwind to Use next/font

app/layout.tsx
import { Inter, JetBrains_Mono } from 'next/font/google';

const inter = Inter({
  subsets: ['latin'],
  variable: '--font-sans',
});

const jetbrainsMono = JetBrains_Mono({
  subsets: ['latin'],
  variable: '--font-mono',
});

export default function RootLayout({ children }) {
  return (
    <html lang="en" className={`${inter.variable} ${jetbrainsMono.variable}`}>
      <body>{children}</body>
    </html>
  );
}
tailwind.config.ts
import type { Config } from 'tailwindcss';

const config: Config = {
  content: ['./app/**/*.{js,ts,jsx,tsx,mdx}'],
  theme: {
    extend: {
      fontFamily: {
        sans: ['var(--font-sans)', 'system-ui', 'sans-serif'],
        mono: ['var(--font-mono)', 'Courier New', 'monospace'],
      },
    },
  },
  plugins: [],
};

export default config;

// ✅ Tailwind uses next/font variables
// ✅ Optimized fonts in utility classes

Using Fonts with Tailwind

app/page.tsx
export default function Home() {
  return (
    <div className="p-8">
      {/* Uses Inter (--font-sans) */}
      <h1 className="text-4xl font-bold mb-4">
        Welcome to Next.js
      </h1>
      
      {/* Uses Inter */}
      <p className="text-lg mb-6">
        This text uses Inter font from next/font.
      </p>
      
      {/* Uses JetBrains Mono (--font-mono) */}
      <code className="font-mono bg-gray-100 px-2 py-1 rounded">
        const hello = "world";
      </code>
    </div>
  );
}

// ✅ font-sans → Inter
// ✅ font-mono → JetBrains Mono
// ✅ Tailwind classes work seamlessly

Font Configuration Options

TYPESCRIPT
import { Inter } from 'next/font/google';

const inter = Inter({
  // Required: Character sets to include
  subsets: ['latin'],
  // Can include: 'latin-ext', 'cyrillic', etc.
  
  // Font weights to load
  weight: ['400', '600', '700'],
  // Or for variable fonts: weight: '100 900'
  
  // Font styles
  style: ['normal', 'italic'],
  
  // Font display strategy
  display: 'swap',
  // Options: 'auto', 'block', 'swap', 'fallback', 'optional'
  
  // Preload font (default: true for first page)
  preload: true,
  
  // CSS variable name
  variable: '--font-inter',
  
  // Fallback fonts
  fallback: ['system-ui', 'arial'],
  
  // Adjust spacing (advanced)
  adjustFontFallback: true,
});

// ✅ Comprehensive configuration
// ✅ Control every aspect of font loading

Display Options Explained

TYPESCRIPT
// display: 'swap' (Recommended)
// Shows fallback immediately, swaps to web font when ready
// ✅ No invisible text
// ✅ Fast initial render
// ⚠️  Slight visual change when font loads

// display: 'optional'
// Only use web font if cached, else use fallback
// ✅ Best performance
// ✅ No layout shift
// ⚠️  Might not show web font on first visit

// display: 'block'
// Hides text until web font loads (up to 3s)
// ⚠️  Invisible text period (bad UX)

// display: 'fallback'
// Short block period, then swap
// Balance between block and swap

// display: 'auto'
// Browser decides strategy

// Recommendation: Use 'swap' for best balance

Complete Font Setup Examples

Example 1: Basic Blog Setup

app/layout.tsx
import { Inter, Merriweather } from 'next/font/google';
import './globals.css';

// Sans-serif for UI
const inter = Inter({
  subsets: ['latin'],
  variable: '--font-sans',
  display: 'swap',
});

// Serif for article content
const merriweather = Merriweather({
  subsets: ['latin'],
  weight: ['400', '700'],
  variable: '--font-serif',
  display: 'swap',
});

export default function RootLayout({ children }) {
  return (
    <html lang="en" className={`${inter.variable} ${merriweather.variable}`}>
      <body className="font-sans">{children}</body>
    </html>
  );
}
app/globals.css
@tailwind base;
@tailwind components;
@tailwind utilities;

@layer base {
  body {
    font-family: var(--font-sans), system-ui, sans-serif;
  }
  
  /* Article content uses serif */
  article {
    font-family: var(--font-serif), Georgia, serif;
    line-height: 1.7;
  }
  
  /* Headings use sans */
  h1, h2, h3, h4, h5, h6 {
    font-family: var(--font-sans), system-ui, sans-serif;
    font-weight: 700;
  }
}

// ✅ Sans for UI elements
// ✅ Serif for readable article content
// ✅ Clear typography hierarchy

Example 2: Complete Design System

app/layout.tsx
import { Inter, Fira_Code, Playfair_Display } from 'next/font/google';

// Primary sans-serif
const inter = Inter({
  subsets: ['latin'],
  variable: '--font-sans',
});

// Monospace for code
const firaCode = Fira_Code({
  subsets: ['latin'],
  variable: '--font-mono',
});

// Display font for headings
const playfair = Playfair_Display({
  subsets: ['latin'],
  weight: ['700'],
  variable: '--font-display',
});

export default function RootLayout({ children }) {
  return (
    <html
      lang="en"
      className={`${inter.variable} ${firaCode.variable} ${playfair.variable}`}
    >
      <body>{children}</body>
    </html>
  );
}
tailwind.config.ts
import type { Config } from 'tailwindcss';

const config: Config = {
  content: ['./app/**/*.{js,ts,jsx,tsx,mdx}'],
  theme: {
    extend: {
      fontFamily: {
        sans: ['var(--font-sans)'],
        mono: ['var(--font-mono)'],
        display: ['var(--font-display)'],
      },
    },
  },
};

export default config;
app/page.tsx
export default function Home() {
  return (
    <div className="container mx-auto px-4 py-8">
      {/* Display font for hero */}
      <h1 className="font-display text-6xl mb-4">
        Beautiful Typography
      </h1>
      
      {/* Sans for body */}
      <p className="font-sans text-lg mb-6">
        This is body text using Inter font.
      </p>
      
      {/* Mono for code */}
      <pre className="font-mono bg-gray-100 p-4 rounded-lg">
        <code>const greeting = "Hello, World!";</code>
      </pre>
    </div>
  );
}

// ✅ font-display → Playfair Display
// ✅ font-sans → Inter
// ✅ font-mono → Fira Code
// ✅ Complete typography system

Example 3: Local Custom Fonts

app/layout.tsx
import localFont from 'next/font/local';

const brandFont = localFont({
  src: [
    {
      path: './fonts/BrandFont-Regular.woff2',
      weight: '400',
      style: 'normal',
    },
    {
      path: './fonts/BrandFont-Bold.woff2',
      weight: '700',
      style: 'normal',
    },
  ],
  variable: '--font-brand',
});

const systemFont = localFont({
  src: './fonts/SystemFont-VF.woff2',
  variable: '--font-system',
  weight: '100 900',
});

export default function RootLayout({ children }) {
  return (
    <html lang="en" className={`${brandFont.variable} ${systemFont.variable}`}>
      <body>{children}</body>
    </html>
  );
}

// ✅ Custom brand fonts
// ✅ Full control over font files
// ✅ Same optimization as Google Fonts

Font Files Structure

Organization of font files and configuration

appImportant
public

Select a file or folder to see details

Font Optimization Best Practices

1. Use Variable Fonts When Possible

TYPESCRIPT
// ✅ GOOD: Variable font (one file, all weights)
const inter = Inter({
  subsets: ['latin'],
  variable: '--font-inter',
});

// ❌ LESS OPTIMAL: Multiple weight files
const inter = Inter({
  subsets: ['latin'],
  weight: ['400', '500', '600', '700', '800'],
});

// Variable fonts are more efficient

2. Preload Critical Fonts Only

TYPESCRIPT
// ✅ GOOD: Preload only critical fonts
const inter = Inter({
  subsets: ['latin'],
  preload: true, // Critical font used immediately
});

const displayFont = Playfair_Display({
  subsets: ['latin'],
  preload: false, // Not critical, load later
});

// Preload only fonts needed for initial render

3. Limit Font Subsets

TYPESCRIPT
// ✅ GOOD: Only include needed character sets
const inter = Inter({
  subsets: ['latin'], // English only
});

// ❌ BAD: Including unnecessary subsets
const inter = Inter({
  subsets: ['latin', 'latin-ext', 'cyrillic', 'greek'],
  // Loads characters you don't need
});

// Smaller bundle = faster loading

4. Use CSS Variables

TYPESCRIPT
// ✅ GOOD: CSS variables for flexibility
const inter = Inter({
  subsets: ['latin'],
  variable: '--font-inter',
});

// Easy to reference in CSS, CSS Modules, Tailwind
// Changes in one place update everywhere

// ❌ LESS FLEXIBLE: Direct className
const inter = Inter({ subsets: ['latin'] });
<body className={inter.className}>
// Harder to override or customize

5. Specify Font Display Strategy

TYPESCRIPT
// ✅ GOOD: Explicit display strategy
const inter = Inter({
  subsets: ['latin'],
  display: 'swap', // Best for most cases
});

// swap = show fallback immediately, swap when ready
// Prevents invisible text, minimal layout shift with size-adjust

Key Takeaways

  • next/font - automatic font optimization built into Next.js
  • Zero layout shift - CSS size-adjust prevents text reflow
  • Self-hosted - no external requests to Google Fonts
  • Google Fonts - import from 'next/font/google'
  • Local fonts - import from 'next/font/local'
  • CSS variables - use 'variable' option for flexibility
  • Variable fonts - one file, all weights (more efficient)
  • Apply in root layout - fonts available app-wide

🎉 Styling Section Complete!

You've completed the Styling in Next.js section! You've mastered:

  • ✅ CSS Modules for component-scoped styling
  • ✅ Tailwind CSS setup and configuration
  • ✅ Global styles and CSS variables
  • ✅ Font optimization with next/font

You now have complete mastery of styling in Next.js! You can use CSS Modules for custom components, Tailwind for rapid development, CSS variables for theming, and next/font for optimized typography. These skills enable you to build beautiful, performant, and maintainable Next.js applications.

⚡ Complete Styling Stack

Combine all styling approaches: CSS Modules for complex components, Tailwind for layout and utilities, CSS variables for theming, and next/font for typography. Each excels at different tasks—use them together for the best results!

Final Quiz: Font Optimization Mastery

Question 1 of 4

What is the main benefit of next/font?

Master next/font for automatic font optimization! Learn Google Fonts, local fonts, and zero layout shift.

Previous
Global Styles and CSS Variables
Next
Next.js Image Component Basics

Continue Your Next.js Journey

Join 2,000+ developers mastering Next.js. Get more advanced tutorials on forms, APIs, and deployment - 100% FREE.

You might also like

UI Components

Ready-made components for your projects

Developer Tools

Boost productivity with handy generators

HTML Game

Learn HTML concepts through interactive gameplay

Join Our Web Development Training Program

Master frontend & backend development with hands-on projects.

NextJS Tutorials

0 of 70 completed

Your Progress0%

Topics

Getting Started

  • What is Next.js?
  • Next.js vs React - Key Differences
  • Creating Your First Next.js 15 Project
  • Next.js Project Structure Explained
  • App Router vs Pages Router

Routing Fundamentals

  • File-Based Routing Basics
  • Creating Pages with page.tsx
  • Dynamic Routes and Route Parameters
  • Catch-All and Optional Catch-All Routes
  • Route Groups for Organization
  • Parallel Routes
  • Intercepting Routes

Layouts and Pages

  • Understanding Layouts
  • Root Layout and Global Configuration
  • Nested Layouts
  • Templates vs Layouts
  • Loading States with loading.tsx
  • Error Handling with error.tsx

Server and Client Components

  • Understanding Server Components
  • Client Components with 'use client'
  • When to Use Server vs Client Components
  • Component Composition Patterns
  • Passing Props Between Server and Client
  • Server Component Patterns and Best Practices

Data Fetching

  • Fetching Data in Server Components
  • Parallel and Sequential Data Fetching
  • Data Fetching Patterns and Strategies
  • Caching and Revalidation
  • Revalidate and Cache Tags
  • Handling Loading and Error States in Data Fetching

Navigation and Links

  • Link Component Basics
  • useRouter Hook for Programmatic Navigation
  • usePathname and useSearchParams Hooks
  • Active Links and Navigation States
  • Redirects and Navigation Guards

Styling in Next.js

  • CSS Modules in Next.js
  • Tailwind CSS Setup and Configuration
  • Global Styles and CSS Variables
  • Next.js Font Optimization

Images and Media

  • Next.js Image Component Basics
  • Image Optimization and Best Practices
  • Working with Static Assets

Forms and Data Mutations

  • Understanding Server Actions
  • Form Handling with Server Actions
  • Form Validation and Error Handling
  • useFormStatus and useFormState Hooks
  • Revalidating Data After Mutations
  • Optimistic Updates

Metadata and SEO

  • Static Metadata Configuration
  • Dynamic Metadata Generation
  • Open Graph and Social Media Cards
  • Sitemap and Robots.txt

API Routes and Route Handlers

  • Route Handlers Introduction
  • GET and POST Request Handlers
  • Dynamic API Routes
  • Request and Response Objects
  • API Error Handling and Status Codes

Middleware and Advanced Features

  • Introduction to Middleware
  • Authentication with Middleware
  • Environment Variables and Configuration
  • Streaming and Suspense
  • Not Found and Global Error Pages

Deployment and Optimization

  • Understanding Static and Dynamic Rendering
  • generateStaticParams for Static Generation
  • Build and Production Optimization
  • Deploying to Vercel
  • Alternative Deployment Options
  • Performance Monitoring and Analytics

Real-World Projects

  • Project 1: E-commerce Product Catalog
  • Project 2: Dashboard with Authentication
Falytom logoFALYTOM

Learn technologies like HTML, CSS, JavaScript, React, NodeJS and more through interactive games, tutorials, components and free tools. Transform your businesses with AI chatbots, SEO-optimized websites, and professional development services.

© 2025 Tomilola Group
  • Facebook logo
  • Twitter logo
  • Instagram logo
  • LinkedIn logo
  • GitHub logo
  • YouTube logo