| name | next-config-optimization |
| description | Use when configuring Next.js builds, optimizing images/fonts/scripts, setting up metadata, or working with Turbopack. Invoke for next.config.js setup, Image component optimization, Font loading, Script strategies, or build configuration. |
| allowed-tools | Read, Grep, Glob |
Next.js Configuration & Optimization Expert
Purpose
Expert knowledge of Next.js configuration and optimization features. Covers next.config.js, Image optimization, Font loading, Script strategies, metadata management, and Turbopack.
When to Use
Invoke this skill when:
- Configuring next.config.js
- Optimizing images with next/image
- Loading fonts efficiently
- Managing third-party scripts
- Setting up metadata for SEO
- Configuring build settings
- Using Turbopack for faster builds
- Implementing environment variables
- Setting up redirects and rewrites
- Debugging build or performance issues
Documentation Available
Location: /Users/zach/Documents/cc-skills/docs/next/
Coverage (~80 files):
Configuration:
- next.config.js options
- Environment variables
- TypeScript configuration
- Compiler options
Image Optimization:
- next/image component
- Image sizing and loading
- Remote images
- Image formats
Font Optimization:
- next/font/google
- next/font/local
- Font loading strategies
- Variable fonts
Script Optimization:
- next/script component
- Script loading strategies
- Third-party scripts
Metadata:
- Static and dynamic metadata
- generateMetadata
- Metadata API
Build Tools:
- Turbopack (experimental)
- Webpack configuration
- Bundle analyzer
Related Skills
- next-app-router: For app/ directory features
- next-data-rendering: For caching and rendering
- next-pages-router: For pages/ directory
Best Practices
- Use next/image - Automatic optimization
- Load fonts with next/font - No layout shift
- Defer non-critical scripts - Use afterInteractive or lazyOnload
- Set proper metadata - SEO and social sharing
- Use environment variables - Secure configuration
- Enable Turbopack - Faster development builds
- Configure image domains - Allowlist remote sources
- Optimize bundle size - Use bundle analyzer
- Set up redirects properly - SEO-friendly
Examples
Example 1: next.config.js Configuration
Question: "How do I configure next.config.js?"
Response:
// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
// React strict mode
reactStrictMode: true,
// Image optimization
images: {
domains: ['example.com', 'cdn.example.com'],
remotePatterns: [
{
protocol: 'https',
hostname: '**.example.com',
port: '',
pathname: '/images/**',
},
],
formats: ['image/avif', 'image/webp'],
deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
minimumCacheTTL: 60,
},
// Redirects
async redirects() {
return [
{
source: '/old-blog/:slug',
destination: '/blog/:slug',
permanent: true, // 308
},
{
source: '/about',
destination: '/',
permanent: false, // 307
},
];
},
// Rewrites
async rewrites() {
return [
{
source: '/api/:path*',
destination: 'https://api.example.com/:path*',
},
];
},
// Headers
async headers() {
return [
{
source: '/:path*',
headers: [
{
key: 'X-Frame-Options',
value: 'DENY',
},
{
key: 'X-Content-Type-Options',
value: 'nosniff',
},
],
},
];
},
// Environment variables (public)
env: {
CUSTOM_KEY: 'my-value',
},
// Compiler options
compiler: {
removeConsole: process.env.NODE_ENV === 'production',
styledComponents: true,
},
// Experimental features
experimental: {
turbo: {
// Turbopack options
},
ppr: true, // Partial Prerendering
},
// Webpack configuration
webpack: (config, { buildId, dev, isServer, defaultLoaders, webpack }) => {
// Custom webpack config
config.plugins.push(
new webpack.DefinePlugin({
'process.env.BUILD_ID': JSON.stringify(buildId),
})
);
return config;
},
// Output configuration
output: 'standalone', // For Docker deployments
// output: 'export', // For static export
// Page extensions
pageExtensions: ['tsx', 'ts', 'jsx', 'js', 'mdx'],
// Trailing slash
trailingSlash: false,
// Base path
basePath: '/docs',
// Asset prefix (CDN)
assetPrefix: process.env.NODE_ENV === 'production'
? 'https://cdn.example.com'
: undefined,
};
module.exports = nextConfig;
TypeScript Configuration:
// tsconfig.json
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"plugins": [
{
"name": "next"
}
],
"paths": {
"@/*": ["./src/*"],
"@/components/*": ["./src/components/*"]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
}
References:
- See:
docs/next/app/api-reference/next-config-js/
Example 2: Image Optimization
Question: "How do I optimize images with next/image?"
Response:
import Image from 'next/image';
// Local image (static import)
import profilePic from '@/public/profile.jpg';
export function ProfileImage() {
return (
<Image
src={profilePic}
alt="Profile picture"
width={500}
height={500}
priority // Load immediately (above fold)
/>
);
}
// Remote image
export function ProductImage({ src }: { src: string }) {
return (
<Image
src={src}
alt="Product"
width={800}
height={600}
quality={90} // 1-100 (default: 75)
placeholder="blur"
blurDataURL="data:image/png;base64,..." // Low-quality placeholder
/>
);
}
// Responsive image
export function ResponsiveImage() {
return (
<Image
src="/product.jpg"
alt="Product"
fill // Fill parent container
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
style={{ objectFit: 'cover' }}
/>
);
}
// With loader (custom CDN)
const customLoader = ({ src, width, quality }) => {
return `https://cdn.example.com/${src}?w=${width}&q=${quality || 75}`;
};
export function CustomLoaderImage() {
return (
<Image
loader={customLoader}
src="product.jpg"
alt="Product"
width={800}
height={600}
/>
);
}
// Lazy loading
export function LazyImage() {
return (
<Image
src="/below-fold.jpg"
alt="Below fold"
width={800}
height={600}
loading="lazy" // Default for non-priority images
/>
);
}
// Background image pattern
export function BackgroundImage() {
return (
<div style={{ position: 'relative', width: '100%', height: '400px' }}>
<Image
src="/background.jpg"
alt="Background"
fill
style={{ objectFit: 'cover' }}
quality={80}
/>
<div style={{ position: 'relative', zIndex: 1 }}>
Content on top of background
</div>
</div>
);
}
Image Formats:
- Automatically serves AVIF/WebP when supported
- Falls back to original format
- Configurable in next.config.js
Performance Benefits:
- Automatic resizing
- Lazy loading by default
- Blur placeholder
- Format optimization
- No layout shift
References:
- See:
docs/next/app/api-reference/components/image/
Example 3: Font Optimization
Question: "How do I load fonts efficiently?"
Response:
// app/layout.tsx
import { Inter, Roboto_Mono, Playfair_Display } from 'next/font/google';
import localFont from 'next/font/local';
// Google Fonts
const inter = Inter({
subsets: ['latin'],
display: 'swap', // Font display strategy
variable: '--font-inter', // CSS variable
});
const robotoMono = Roboto_Mono({
subsets: ['latin'],
weight: ['400', '700'],
variable: '--font-roboto-mono',
});
const playfair = Playfair_Display({
subsets: ['latin'],
weight: ['400', '700'],
style: ['normal', 'italic'],
variable: '--font-playfair',
});
// Local fonts
const myFont = localFont({
src: [
{
path: './fonts/MyFont-Regular.woff2',
weight: '400',
style: 'normal',
},
{
path: './fonts/MyFont-Bold.woff2',
weight: '700',
style: 'normal',
},
],
variable: '--font-my-font',
});
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html
lang="en"
className={`${inter.variable} ${robotoMono.variable} ${playfair.variable} ${myFont.variable}`}
>
<body className={inter.className}>{children}</body>
</html>
);
}
// Use in CSS
// globals.css
/*
body {
font-family: var(--font-inter);
}
code {
font-family: var(--font-roboto-mono);
}
h1 {
font-family: var(--font-playfair);
}
*/
// Variable fonts
import { Inter } from 'next/font/google';
const inter = Inter({
subsets: ['latin'],
display: 'swap',
variable: '--font-inter',
// Variable fonts support weight range
});
// Preload fonts
const inter = Inter({
subsets: ['latin'],
preload: true, // Default: true
});
// Adjust font display
const inter = Inter({
subsets: ['latin'],
display: 'swap', // or 'auto', 'block', 'fallback', 'optional'
});
Benefits:
- Zero layout shift (font metrics known)
- Self-hosted (privacy, performance)
- Automatic subsetting
- CSS variables for flexibility
- Optimal loading strategy
References:
- See:
docs/next/app/api-reference/components/font/
Example 4: Script Optimization
Question: "How do I load third-party scripts efficiently?"
Response:
import Script from 'next/script';
// Global scripts (app/layout.tsx)
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body>
{/* Google Analytics */}
<Script
src="https://www.googletagmanager.com/gtag/js?id=GA_MEASUREMENT_ID"
strategy="afterInteractive" // Load after page interactive
/>
<Script id="google-analytics" strategy="afterInteractive">
{`
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'GA_MEASUREMENT_ID');
`}
</Script>
{children}
</body>
</html>
);
}
// Page-specific script
export default function ContactPage() {
return (
<div>
<h1>Contact Us</h1>
<form id="contact-form">{/* Form fields */}</form>
{/* Load script only on this page */}
<Script
src="https://example.com/form-validator.js"
strategy="lazyOnload" // Load after everything else
onLoad={() => {
console.log('Script loaded');
}}
onError={(e) => {
console.error('Script failed to load', e);
}}
/>
</div>
);
}
// Inline script
export function InlineScriptExample() {
return (
<>
<Script id="config" strategy="beforeInteractive">
{`
window.APP_CONFIG = {
apiUrl: '${process.env.NEXT_PUBLIC_API_URL}',
env: '${process.env.NODE_ENV}',
};
`}
</Script>
</>
);
}
// Worker script
export function WorkerScriptExample() {
return (
<Script
src="/worker.js"
strategy="worker" // Experimental: run in web worker
/>
);
}
Strategy Options:
beforeInteractive: Load before page interactive (critical scripts)afterInteractive: Load after page interactive (default)lazyOnload: Load during idle timeworker: Run in web worker (experimental)
References:
- See:
docs/next/app/api-reference/components/script/
Example 5: Metadata Management
Question: "How do I set up metadata for SEO?"
Response:
// app/layout.tsx (static metadata)
import type { Metadata } from 'next';
export const metadata: Metadata = {
title: {
template: '%s | My App',
default: 'My App - Best Product Ever',
},
description: 'The best app for doing things',
keywords: ['next.js', 'react', 'typescript'],
authors: [{ name: 'John Doe', url: 'https://example.com' }],
creator: 'John Doe',
publisher: 'My Company',
metadataBase: new URL('https://example.com'),
openGraph: {
type: 'website',
locale: 'en_US',
url: 'https://example.com',
siteName: 'My App',
title: 'My App',
description: 'The best app for doing things',
images: [
{
url: '/og-image.jpg',
width: 1200,
height: 630,
alt: 'My App Preview',
},
],
},
twitter: {
card: 'summary_large_image',
site: '@myapp',
creator: '@johndoe',
title: 'My App',
description: 'The best app for doing things',
images: ['/twitter-image.jpg'],
},
robots: {
index: true,
follow: true,
googleBot: {
index: true,
follow: true,
'max-video-preview': -1,
'max-image-preview': 'large',
'max-snippet': -1,
},
},
icons: {
icon: '/favicon.ico',
apple: '/apple-touch-icon.png',
},
manifest: '/site.webmanifest',
};
// app/products/[id]/page.tsx (dynamic metadata)
import { Metadata } from 'next';
interface Props {
params: { id: string };
}
export async function generateMetadata({ params }: Props): Promise<Metadata> {
const product = await fetch(`https://api.example.com/products/${params.id}`)
.then((r) => r.json());
return {
title: product.name,
description: product.description,
openGraph: {
title: product.name,
description: product.description,
images: [product.image],
type: 'product',
},
twitter: {
card: 'summary_large_image',
title: product.name,
description: product.description,
images: [product.image],
},
};
}
export default function ProductPage({ params }: Props) {
return <div>{/* Product content */}</div>;
}
// JSON-LD structured data
export function ProductStructuredData({ product }) {
const jsonLd = {
'@context': 'https://schema.org',
'@type': 'Product',
name: product.name,
description: product.description,
image: product.image,
offers: {
'@type': 'Offer',
price: product.price,
priceCurrency: 'USD',
},
};
return (
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
/>
);
}
References:
- See:
docs/next/app/api-reference/functions/generate-metadata/
Common Patterns
Environment Variables
// .env.local
DATABASE_URL=postgres://localhost:5432/db
NEXT_PUBLIC_API_URL=https://api.example.com
// Access in code
const dbUrl = process.env.DATABASE_URL; // Server only
const apiUrl = process.env.NEXT_PUBLIC_API_URL; // Client + server
Bundle Analyzer
npm install @next/bundle-analyzer
# next.config.js
const withBundleAnalyzer = require('@next/bundle-analyzer')({
enabled: process.env.ANALYZE === 'true',
});
module.exports = withBundleAnalyzer(nextConfig);
# Run analysis
ANALYZE=true npm run build
Turbopack
# Development with Turbopack
next dev --turbo
# Production build (not ready yet)
# next build --turbo
Search Helpers
# Find config docs
grep -r "next.config\|configuration" /Users/zach/Documents/cc-skills/docs/next/
# Find optimization docs
grep -r "Image\|Font\|Script\|optimization" /Users/zach/Documents/cc-skills/docs/next/
# Find metadata docs
grep -r "metadata\|SEO\|generateMetadata" /Users/zach/Documents/cc-skills/docs/next/
# List config files
ls /Users/zach/Documents/cc-skills/docs/next/app/api-reference/
Common Errors
Image domain not configured: Remote image source not allowed
- Solution: Add domain to
images.domainsin next.config.js
- Solution: Add domain to
Font not loading: Wrong path or subset
- Solution: Check font file path and subset configuration
Metadata not updating: Using static metadata for dynamic page
- Solution: Use generateMetadata function
Build fails: Invalid next.config.js syntax
- Solution: Check syntax and required fields
Performance Tips
- Use next/image - Automatic optimization
- Load fonts with next/font - Zero layout shift
- Defer non-critical scripts - Use lazyOnload
- Enable Turbopack - Faster dev builds (7x faster)
- Use static metadata - When possible
- Optimize images - Set quality and sizes
- Analyze bundle - Find large dependencies
- Use CDN for assets - Set assetPrefix
Notes
- Documentation covers Next.js 13-14
- Turbopack is experimental in Next.js 14
- next/image requires width/height or fill
- next/font automatically self-hosts Google Fonts
- Environment variables starting with NEXT_PUBLIC_ are exposed to browser
- Metadata is merged from layout to page
- File paths reference local documentation cache
- For latest updates, check https://nextjs.org/docs/app/api-reference/next-config-js