246 lines
5.1 KiB
Markdown
246 lines
5.1 KiB
Markdown
# Font Optimization
|
|
|
|
Use `next/font` for automatic font optimization with zero layout shift.
|
|
|
|
## Google Fonts
|
|
|
|
```tsx
|
|
// app/layout.tsx
|
|
import { Inter } from 'next/font/google'
|
|
|
|
const inter = Inter({ subsets: ['latin'] })
|
|
|
|
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
|
return (
|
|
<html lang="en" className={inter.className}>
|
|
<body>{children}</body>
|
|
</html>
|
|
)
|
|
}
|
|
```
|
|
|
|
## Multiple Fonts
|
|
|
|
```tsx
|
|
import { Inter, Roboto_Mono } from 'next/font/google'
|
|
|
|
const inter = Inter({
|
|
subsets: ['latin'],
|
|
variable: '--font-inter',
|
|
})
|
|
|
|
const robotoMono = Roboto_Mono({
|
|
subsets: ['latin'],
|
|
variable: '--font-roboto-mono',
|
|
})
|
|
|
|
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
|
return (
|
|
<html lang="en" className={`${inter.variable} ${robotoMono.variable}`}>
|
|
<body>{children}</body>
|
|
</html>
|
|
)
|
|
}
|
|
```
|
|
|
|
Use in CSS:
|
|
```css
|
|
body {
|
|
font-family: var(--font-inter);
|
|
}
|
|
|
|
code {
|
|
font-family: var(--font-roboto-mono);
|
|
}
|
|
```
|
|
|
|
## Font Weights and Styles
|
|
|
|
```tsx
|
|
// Single weight
|
|
const inter = Inter({
|
|
subsets: ['latin'],
|
|
weight: '400',
|
|
})
|
|
|
|
// Multiple weights
|
|
const inter = Inter({
|
|
subsets: ['latin'],
|
|
weight: ['400', '500', '700'],
|
|
})
|
|
|
|
// Variable font (recommended) - includes all weights
|
|
const inter = Inter({
|
|
subsets: ['latin'],
|
|
// No weight needed - variable fonts support all weights
|
|
})
|
|
|
|
// With italic
|
|
const inter = Inter({
|
|
subsets: ['latin'],
|
|
style: ['normal', 'italic'],
|
|
})
|
|
```
|
|
|
|
## Local Fonts
|
|
|
|
```tsx
|
|
import localFont from 'next/font/local'
|
|
|
|
const myFont = localFont({
|
|
src: './fonts/MyFont.woff2',
|
|
})
|
|
|
|
// Multiple files for different weights
|
|
const myFont = localFont({
|
|
src: [
|
|
{
|
|
path: './fonts/MyFont-Regular.woff2',
|
|
weight: '400',
|
|
style: 'normal',
|
|
},
|
|
{
|
|
path: './fonts/MyFont-Bold.woff2',
|
|
weight: '700',
|
|
style: 'normal',
|
|
},
|
|
],
|
|
})
|
|
|
|
// Variable font
|
|
const myFont = localFont({
|
|
src: './fonts/MyFont-Variable.woff2',
|
|
variable: '--font-my-font',
|
|
})
|
|
```
|
|
|
|
## Tailwind CSS Integration
|
|
|
|
```tsx
|
|
// app/layout.tsx
|
|
import { Inter } from 'next/font/google'
|
|
|
|
const inter = Inter({
|
|
subsets: ['latin'],
|
|
variable: '--font-inter',
|
|
})
|
|
|
|
export default function RootLayout({ children }) {
|
|
return (
|
|
<html lang="en" className={inter.variable}>
|
|
<body>{children}</body>
|
|
</html>
|
|
)
|
|
}
|
|
```
|
|
|
|
```js
|
|
// tailwind.config.js
|
|
module.exports = {
|
|
theme: {
|
|
extend: {
|
|
fontFamily: {
|
|
sans: ['var(--font-inter)'],
|
|
},
|
|
},
|
|
},
|
|
}
|
|
```
|
|
|
|
## Preloading Subsets
|
|
|
|
Only load needed character subsets:
|
|
|
|
```tsx
|
|
// Latin only (most common)
|
|
const inter = Inter({ subsets: ['latin'] })
|
|
|
|
// Multiple subsets
|
|
const inter = Inter({ subsets: ['latin', 'latin-ext', 'cyrillic'] })
|
|
```
|
|
|
|
## Display Strategy
|
|
|
|
Control font loading behavior:
|
|
|
|
```tsx
|
|
const inter = Inter({
|
|
subsets: ['latin'],
|
|
display: 'swap', // Default - shows fallback, swaps when loaded
|
|
})
|
|
|
|
// Options:
|
|
// 'auto' - browser decides
|
|
// 'block' - short block period, then swap
|
|
// 'swap' - immediate fallback, swap when ready (recommended)
|
|
// 'fallback' - short block, short swap, then fallback
|
|
// 'optional' - short block, no swap (use if font is optional)
|
|
```
|
|
|
|
## Don't Use Manual Font Links
|
|
|
|
Always use `next/font` instead of `<link>` tags for Google Fonts.
|
|
|
|
```tsx
|
|
// Bad: Manual link tag (blocks rendering, no optimization)
|
|
<link href="https://fonts.googleapis.com/css2?family=Inter" rel="stylesheet" />
|
|
|
|
// Bad: Missing display and preconnect
|
|
<link href="https://fonts.googleapis.com/css2?family=Inter" rel="stylesheet" />
|
|
|
|
// Good: Use next/font (self-hosted, zero layout shift)
|
|
import { Inter } from 'next/font/google'
|
|
|
|
const inter = Inter({ subsets: ['latin'] })
|
|
```
|
|
|
|
## Common Mistakes
|
|
|
|
```tsx
|
|
// Bad: Importing font in every component
|
|
// components/Button.tsx
|
|
import { Inter } from 'next/font/google'
|
|
const inter = Inter({ subsets: ['latin'] }) // Creates new instance each time!
|
|
|
|
// Good: Import once in layout, use CSS variable
|
|
// app/layout.tsx
|
|
const inter = Inter({ subsets: ['latin'], variable: '--font-inter' })
|
|
|
|
// Bad: Using @import in CSS (blocks rendering)
|
|
/* globals.css */
|
|
@import url('https://fonts.googleapis.com/css2?family=Inter');
|
|
|
|
// Good: Use next/font (self-hosted, no network request)
|
|
import { Inter } from 'next/font/google'
|
|
|
|
// Bad: Loading all weights when only using a few
|
|
const inter = Inter({ subsets: ['latin'] }) // Loads all weights
|
|
|
|
// Good: Specify only needed weights (for non-variable fonts)
|
|
const inter = Inter({ subsets: ['latin'], weight: ['400', '700'] })
|
|
|
|
// Bad: Missing subset - loads all characters
|
|
const inter = Inter({})
|
|
|
|
// Good: Always specify subset
|
|
const inter = Inter({ subsets: ['latin'] })
|
|
```
|
|
|
|
## Font in Specific Components
|
|
|
|
```tsx
|
|
// For component-specific fonts, export from a shared file
|
|
// lib/fonts.ts
|
|
import { Inter, Playfair_Display } from 'next/font/google'
|
|
|
|
export const inter = Inter({ subsets: ['latin'], variable: '--font-inter' })
|
|
export const playfair = Playfair_Display({ subsets: ['latin'], variable: '--font-playfair' })
|
|
|
|
// components/Heading.tsx
|
|
import { playfair } from '@/lib/fonts'
|
|
|
|
export function Heading({ children }) {
|
|
return <h1 className={playfair.className}>{children}</h1>
|
|
}
|
|
```
|