Налаштування Next.js (next.config.js)
next.config.js - це Node.js-модуль у корені проєкту, який Next.js читає один раз при запуску для налаштування збірки, роутингу, оптимізації зображень та поведінки при деплої.
Теорія
TL;DR
- Уяви панель управління автомобіля: налаштував один раз, і кожна поїздка використовує ці параметри
- Next.js читає
next.config.jsчерезrequire()при старті, не при кожному запиті - Зміни в конфігурації вимагають перезапуску dev-сервера (
next dev/next build) - Для клієнтських змінних середовища потрібен префікс
NEXT_PUBLIC_або явний ключenv; секрети туди не кладуть - Формат за замовчуванням - CJS (
module.exports); Next.js 14.2+ підтримуєnext.config.tsз повними типами
Швидкий приклад
// next.config.js - базове налаштування
/** @type {import('next').NextConfig} */
const nextConfig = {
images: {
remotePatterns: [{ protocol: 'https', hostname: '**.example.com' }]
},
basePath: '/my-app', // /about стає /my-app/about
reactStrictMode: true,
};
module.exports = nextConfig;Після next dev: зображення з *.example.com проходять через оптимізацію Next.js, а всі роути автоматично отримують префікс /my-app.
Як Next.js завантажує конфігурацію
Next.js CLI викликає require() для next.config.js один раз за сесію або при збірці. Він перевіряє об'єкт, що експортується, та передає значення в компілятори SWC/Webpack і серверний рантайм. Значення з ключа env потрапляють через DefinePlugin Webpack, який вбудовує їх як рядкові літерали в бандл. Саме тому вони фіксуються під час збірки, а не при кожному запиті.
Hot reload стежить за файлом і перезапускає dev-сервер при змінах. Під час next build конфігурація серіалізується для статичного експорту, якщо встановлено output: 'export'.
Основні опції
Зображення. Ключ images керує оптимізацією зображень (image optimization). Використовуй remotePatterns замість застарілого domains, щоб дозволити зовнішні джерела:
images: {
remotePatterns: [
{ protocol: 'https', hostname: 'cdn.shopify.com', pathname: '/**' },
{ protocol: 'https', hostname: 'images.unsplash.com' },
],
minimumCacheTTL: 60,
formats: ['image/avif', 'image/webp'],
},remotePatterns дозволяє обмежити доступ за протоколом і шляхом, чого domains не вмів, тому його й прибрали в Next.js 13.
Redirects і rewrites. Redirect відправляє браузер на нову URL з кодом статусу. Rewrite проксіює запит непомітно, зберігаючи оригінальну адресу для користувача:
async redirects() {
return [
{ source: '/old-blog/:slug', destination: '/blog/:slug', permanent: true },
];
},
async rewrites() {
return [
{ source: '/api/:path*', destination: 'https://api.example.com/:path*' },
];
},Більшість продакшн-додатків на Next.js в підсумку мають хоча б один rewrite. Це найпростіший спосіб тримати API-ключі на сервері й уникнути CORS, не чіпаючи конфігурацію бекенду.
Заголовки (headers). Додавай заголовки безпеки глобально або для конкретних маршрутів:
async headers() {
return [
{
source: '/(.*)',
headers: [
{ key: 'X-Frame-Options', value: 'DENY' },
{ key: 'X-Content-Type-Options', value: 'nosniff' },
],
},
];
},Output та деплой. output: 'standalone' пакує тільки необхідні файли, що підходить для більшості Docker-сетапів. output: 'export' генерує повністю статичний сайт для S3, GitHub Pages або статичного тарифу Vercel.
Змінні середовища. Два способи відкрити змінні для клієнта:
env: { API_URL: process.env.API_URL }Це вбудовує значення в клієнтський бандл. Для серверних змінних просто пропусти цей ключ і звертайся до process.env напряму всередині server components або API-роутів.
Коли що використовувати
- Зовнішні зображення з CDN або CMS:
images.remotePatterns - Проксування API для обходу CORS:
rewrites - Старі URL, які назавжди переїхали:
redirectsзpermanent: true - Заголовки безпеки (CSP, HSTS):
headers - Docker-деплой:
output: 'standalone' - Хостинг у піддиректорії (наприклад, за Nginx на
/app):basePath - Сторонні пакети, що ламають RSC:
experimental.serverComponentsExternalPackages - MDX або нестандартні розширення файлів:
pageExtensions: ['js', 'jsx', 'md', 'mdx']
Типові помилки
Зміна конфігурації без перезапуску dev-сервера. next.config.js завантажується один раз. Якщо додав remotePatterns, а зображення все одно не вантажяться, причина саме в цьому. Перезапусти next dev. З Turbopack у Next.js 15+ (next dev --turbo) перезапуски швидші, але для змін конфігурації все одно потрібні.
Витік секретів через env.
// Неправильно - DB_PASS потрапить у клієнтський JavaScript
env: { DB_PASS: process.env.DB_PASS }Для серверних даних звертайся до process.env.DB_PASS всередині server component. Префікс NEXT_PUBLIC_ існує тільки для дійсно публічних значень.
Wildcard-хостнейм у remotePatterns.
// Неправильно - Next.js 13+ відхиляє '*' як hostname
remotePatterns: [{ hostname: '*' }]Вказуй конкретні хостнейми. Для піддоменів використовуй ** як префікс: hostname: '**.example.com'.
Async-конфігурація в CommonJS. module.exports = async () => ({}) ламається в старих версіях Next.js. Використовуй синхронний експорт у .js-файлах, або перейди на next.config.mjs (ESM), якщо потрібна асинхронна логіка.
typescript.ignoreBuildErrors: true в продакшені. Це пропускає перевірку типів під час next build і відправляє зламані типи до користувачів. Тримай значення false або виправляй реальні помилки.
Де використовується
- Vercel Commerce / Shopify Hydrogen:
images.remotePatternsдля CDN +rewritesдля Storefront API - T3 Stack (tRPC + Next.js):
experimental.serverComponentsExternalPackages: ['@trpc/server'] - Сайти з контентом на MDX:
pageExtensions: ['js', 'jsx', 'md', 'mdx'] - Docker-деплой:
output: 'standalone' - Supabase-проєкти:
rewritesдля проксування edge functions без CORS
Питання на співбесіді
Q: Яка різниця між images.domains і images.remotePatterns?
A: domains (застаріло з Next.js 13) дозволяє будь-який шлях на хостнеймі без додаткових фільтрів. remotePatterns обмежує за протоколом, шляхом і портом, що безпечніше. Завжди використовуй remotePatterns.
Q: Як next.config.js взаємодіє з Turbopack?
A: Turbopack (стабільний у Next.js 15) ігнорує більшість webpack-ключів. Щоб додати кастомні лоадери, використовуй turbo.rules: turbo: { rules: { '*.sql': { loaders: ['sql-loader'] } } }.
Q: Чи може next.config.js експортувати async-функцію?
A: У CommonJS (.js) надійно працює тільки синхронний експорт. У ESM (next.config.mjs) або TypeScript (next.config.ts, Next.js 14.2+) async підтримується, але I/O-операцій під час збірки краще уникати.
Q: Чому зміна basePath ламає імпорти статичних файлів?
A: Статичні файли з /public теж потребують префікса basePath. next/image обробляє це автоматично, а сирий <img src="/logo.png"> - ні. Фіксується через assetPrefix або заміну на компонент next/image.
Q: Як уникнути дублювання конфігурації в монорепо з кількома Next.js-додатками?
A: Виноси спільні опції в пакет (наприклад, packages/next-config/base.mjs) і розкидай їх спредом: import base from '@acme/next-config'; export default { ...base, ...localOverrides };. Саме такий патерн використовується в стартерах Turborepo для Next.js.
Приклади
Базовий: CDN-зображення та API-проксі
// next.config.js - типове налаштування для продакшену
/** @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 [
// Браузер звертається до /api/products, Next.js пересилає на бекенд
{ source: '/api/:path*', destination: 'https://api.example.com/:path*' },
];
},
};
module.exports = nextConfig;/api/products на клієнті вирішується без CORS-помилок. Зображення Shopify автоматично ресайзяться та роздаються у форматах WebP або AVIF.
Середній: Docker, заголовки безпеки та 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', // Мінімальний Docker-образ
reactStrictMode: true,
async headers() {
return [
{
source: '/(.*)',
headers: [
{ key: 'X-Frame-Options', value: 'DENY' },
{ key: 'X-Content-Type-Options', value: 'nosniff' },
// HSTS тільки в продакшені, бо dev використовує HTTP
...(isProd
? [{ key: 'Strict-Transport-Security', value: 'max-age=63072000' }]
: []),
],
},
];
},
typescript: {
ignoreBuildErrors: false, // Ніколи не ставити true в продакшені
},
};
export default nextConfig;У dev HSTS пропускається, щоб localhost продовжував працювати по HTTP. У продакшені заголовок примушує використовувати HTTPS упродовж 2 років. standalone робить Docker-образ мінімальним.
Просунутий: спільна конфігурація в монорепо
// packages/next-config/base.mjs - спільна конфігурація для всіх додатків
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*' },
];
},
};Кожен додаток у монорепо розширює спільну конфігурацію без дублювання reactStrictMode, formats та experimental. Змінна API_URL вказує на різні бекенди залежно від середовища.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.