Serve optimized images with next/image, self-host fonts with next/font, and load third-party scripts with next/script.
Why: the <Image> component resizes images, serves modern formats, and lazy-loads them (only loads when scrolled into view) — all automatically. For local images, give width and height to reserve space and avoid layout jumps as they load.
import Image from 'next/image'
export default function Avatar() {
return (
<Image
src="/profile.png" // file in the public/ folder
alt="Picture of the author"
width={500}
height={500}
/>
)
}Why: if you import a local image file, Next.js reads its real width and height for you. For images on another domain, pass width/height yourself and allow the host in next.config.ts — an allowlist that prevents abuse.
// Static import — dimensions detected automatically
import Image from 'next/image'
import profile from './profile.png'
export function Me() {
return <Image src={profile} alt="Me" placeholder="blur" />
}
// next.config.ts — allow a remote host
const config = {
images: {
remotePatterns: [{ protocol: 'https', hostname: 's3.amazonaws.com' }],
},
}Why: next/font downloads a Google Font at build time and serves it from your own domain — faster, more private (no request to Google), and with no layout shift. Apply it by spreading its className onto an element.
// app/layout.tsx
import { Geist } from 'next/font/google'
const geist = Geist({ subsets: ['latin'] })
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en" className={geist.className}>
<body>{children}</body>
</html>
)
}Why: to use a font file you ship yourself, use next/font/local and point src at the file. Same benefits — self-hosted and zero layout shift.
import localFont from 'next/font/local'
const myFont = localFont({ src: './fonts/MyFont.woff2' })
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en" className={myFont.className}>
<body>{children}</body>
</html>
)
}Why: <Script> loads external scripts (analytics, widgets) without hurting performance. The strategy prop controls timing — "afterInteractive" loads it after the page becomes usable, so it doesn’t block your content.
import Script from 'next/script'
export default function Layout({ children }: { children: React.ReactNode }) {
return (
<>
{children}
<Script
src="https://example.com/analytics.js"
strategy="afterInteractive"
/>
</>
)
}