Suggest an editImprove this articleRefine the answer for “Configuring Next.js (next.config.js)”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)**next.config.js** is the configuration file Next.js reads once at startup to control image optimization, URL rewrites, HTTP headers, env vars, and deployment output. ```javascript const nextConfig = { images: { remotePatterns: [{ protocol: 'https', hostname: 'cdn.example.com' }] }, output: 'standalone', }; module.exports = nextConfig; ``` **Key:** changes require a dev server restart; values are baked in at build time, not evaluated per request.Shown above the full answer for quick recall.Answer (EN)Image**next.config.js** is a Node.js module at your project root that Next.js reads once at startup to configure bundling, routing, image optimization, and deployment behavior. ## Theory ### TL;DR - Think of it like a car dashboard: set it once, every build uses those settings - Next.js reads `next.config.js` via `require()` at startup, not on every request - Changes require a dev server restart (`next dev` / `next build`) - Client-side env vars need the `NEXT_PUBLIC_` prefix or an explicit `env` key; never put secrets there - Default format is CJS (`module.exports`); Next.js 14.2+ supports `next.config.ts` with full type checking ### Quick example ```javascript // next.config.js - basic production setup /** @type {import('next').NextConfig} */ const nextConfig = { images: { remotePatterns: [{ protocol: 'https', hostname: '**.example.com' }] }, basePath: '/my-app', // /about becomes /my-app/about reactStrictMode: true, }; module.exports = nextConfig; ``` After `next dev`: images from `*.example.com` load through Next.js optimization, and all routes gain the `/my-app` prefix automatically. ### How Next.js loads the config Next.js CLI calls Node.js `require()` on `next.config.js` once per dev session or build. It validates the exported object, then passes values into SWC/Webpack compilers and the server runtime. Values from the `env` key go through Webpack's `DefinePlugin`, which inlines them as string literals in the bundle. That is why they are fixed at build time, not evaluated per request. Hot reload watches the file and restarts the dev server on changes. During `next build`, the config is serialized for static export if `output: 'export'` is set. ### Core options **Images.** The `images` key controls Next.js image optimization. Use `remotePatterns` (not the deprecated `domains`) to allow external sources: ```javascript images: { remotePatterns: [ { protocol: 'https', hostname: 'cdn.shopify.com', pathname: '/**' }, { protocol: 'https', hostname: 'images.unsplash.com' }, ], minimumCacheTTL: 60, formats: ['image/avif', 'image/webp'], }, ``` `remotePatterns` adds protocol and pathname filters that `domains` never had, which is why `domains` was deprecated in Next.js 13. **Redirects and rewrites.** A redirect sends the browser to a new URL with a status code. A rewrite proxies the request without changing the URL the user sees: ```javascript async redirects() { return [ { source: '/old-blog/:slug', destination: '/blog/:slug', permanent: true }, ]; }, async rewrites() { return [ { source: '/api/:path*', destination: 'https://api.example.com/:path*' }, ]; }, ``` The rewrite-based API proxy is the pattern most production Next.js apps end up using. It keeps API keys server-side and avoids CORS without touching the backend configuration at all. **Headers.** Add security headers globally or per route: ```javascript async headers() { return [ { source: '/(.*)', headers: [ { key: 'X-Frame-Options', value: 'DENY' }, { key: 'X-Content-Type-Options', value: 'nosniff' }, ], }, ]; }, ``` **Output and deployment.** `output: 'standalone'` bundles only the files your app needs, which is what most Docker setups use. `output: 'export'` generates a fully static site for S3, GitHub Pages, or Vercel's static tier. **Environment variables.** Two ways to expose vars at build time: ```javascript env: { API_URL: process.env.API_URL } ``` This inlines the value into the client bundle. For server-only vars, skip this key entirely and read `process.env` directly inside server components or API routes. ### When to use each option - External images from a CDN or CMS: `images.remotePatterns` - API proxy to avoid CORS: `rewrites` - Old URLs that moved permanently: `redirects` with `permanent: true` - Security headers (CSP, HSTS): `headers` - Docker deployment: `output: 'standalone'` - Subdirectory hosting (e.g., behind Nginx at `/app`): `basePath` - Third-party packages that break RSC: `experimental.serverComponentsExternalPackages` - MDX or custom file extensions: `pageExtensions: ['js', 'jsx', 'md', 'mdx']` ### Common mistakes **Editing config without restarting the dev server.** `next.config.js` loads once. If you add `remotePatterns` and images still fail, that is why. Restart `next dev`. With Turbopack in Next.js 15+ (`next dev --turbo`), restarts are faster but still required for config changes. **Exposing secrets through `env`.** ```javascript // Wrong - this bundles DB_PASS into the client JavaScript env: { DB_PASS: process.env.DB_PASS } ``` Access server-only values via `process.env.DB_PASS` inside a server component. The `NEXT_PUBLIC_` prefix exists for values that are genuinely safe to expose. **Wildcard hostname in `remotePatterns`.** ```javascript // Wrong - Next.js 13+ rejects '*' as a hostname remotePatterns: [{ hostname: '*' }] ``` List exact hostnames. For subdomains, use `**` as a prefix: `hostname: '**.example.com'`. **Async config in CommonJS.** `module.exports = async () => ({})` crashes in older Next.js versions. Use a sync export in `.js` files, or move to `next.config.mjs` (ESM) if you need async logic. **Setting `typescript.ignoreBuildErrors: true` in production.** This skips type checking during `next build` and ships broken code to users. Keep it `false` and fix actual errors. ### Real-world usage - Vercel Commerce / Shopify Hydrogen: `images.remotePatterns` for CDN assets + `rewrites` for the Storefront API - T3 Stack (tRPC + Next.js): `experimental.serverComponentsExternalPackages: ['@trpc/server']` - Content sites with MDX: `pageExtensions: ['js', 'jsx', 'md', 'mdx']` - Docker deployments: `output: 'standalone'` - Supabase apps: `rewrites` to proxy edge functions without CORS ### Follow-up questions **Q:** What is the difference between `images.domains` and `images.remotePatterns`? **A:** `domains` (deprecated in Next.js 13) allows any path on a hostname with no filters. `remotePatterns` lets you restrict by protocol, pathname, and port. Always use `remotePatterns`. **Q:** How does `next.config.js` interact with Turbopack? **A:** Turbopack (stable in Next.js 15) ignores most webpack config keys. To add custom loaders, use `turbo.rules` instead: `turbo: { rules: { '*.sql': { loaders: ['sql-loader'] } } }`. **Q:** Can `next.config.js` export an async function? **A:** In CommonJS (`.js`), only sync exports work reliably. In ESM (`next.config.mjs`) or TypeScript (`next.config.ts`, Next.js 14.2+), async exports are supported, but avoid I/O operations at build time. **Q:** Why does changing `basePath` break static asset imports? **A:** Static files in `/public` need the `basePath` prefix too. `next/image` handles this automatically, but a raw `<img src="/logo.png">` does not. Fix it with `assetPrefix` or switch to the `next/image` component. **Q:** In a monorepo with multiple Next.js apps, how do you share config without duplication? **A:** Extract shared options into a package (e.g., `packages/next-config/base.mjs`) and spread them: `import base from '@acme/next-config'; export default { ...base, ...localOverrides };`. This is the pattern used in Turborepo's Next.js starters. ## Examples ### Basic: image CDN and API proxy ```javascript // next.config.js - common production setup /** @type {import('next').NextConfig} */ const nextConfig = { reactStrictMode: true, images: { remotePatterns: [ { protocol: 'https', hostname: 'images.unsplash.com' }, { protocol: 'https', hostname: 'cdn.shopify.com', pathname: '/**' }, ], minimumCacheTTL: 60, }, async rewrites() { return [ // Browser calls /api/products, Next.js forwards to the backend { source: '/api/:path*', destination: 'https://api.example.com/:path*' }, ]; }, }; module.exports = nextConfig; ``` `/api/products` on the client resolves without CORS errors. Shopify images are auto-resized and served in WebP or AVIF format. ### Intermediate: Docker output, security headers, and TypeScript config ```typescript // next.config.ts (Next.js 14.2+) import type { NextConfig } from 'next'; const isProd = process.env.NODE_ENV === 'production'; const nextConfig: NextConfig = { output: 'standalone', // Minimal Docker image reactStrictMode: true, async headers() { return [ { source: '/(.*)', headers: [ { key: 'X-Frame-Options', value: 'DENY' }, { key: 'X-Content-Type-Options', value: 'nosniff' }, // Skip HSTS in dev - localhost uses HTTP ...(isProd ? [{ key: 'Strict-Transport-Security', value: 'max-age=63072000' }] : []), ], }, ]; }, typescript: { ignoreBuildErrors: false, // Never set to true in production }, }; export default nextConfig; ``` In dev, HSTS is skipped so localhost still works over HTTP. In production, the header enforces HTTPS for 2 years. The `standalone` output keeps the Docker image small by including only what the app actually uses. ### Advanced: shared config across a monorepo ```javascript // packages/next-config/base.mjs - shared across all apps export const baseConfig = { reactStrictMode: true, images: { formats: ['image/avif', 'image/webp'], }, experimental: { optimizePackageImports: ['lucide-react'], }, }; // apps/storefront/next.config.mjs import { baseConfig } from '@acme/next-config'; /** @type {import('next').NextConfig} */ export default { ...baseConfig, images: { ...baseConfig.images, remotePatterns: [ { protocol: 'https', hostname: 'cdn.shopify.com', pathname: '/**' }, ], }, async rewrites() { return [ { source: '/api/:path*', destination: process.env.API_URL + '/:path*' }, ]; }, }; ``` Each app in the monorepo extends the shared config without repeating `reactStrictMode`, `formats`, or `experimental`. The `API_URL` env var points to a different backend per environment.For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.