Skip to main content

Налаштування 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 з повними типами

Швидкий приклад

javascript
// 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, щоб дозволити зовнішні джерела:

javascript
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 проксіює запит непомітно, зберігаючи оригінальну адресу для користувача:

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*' }, ]; },

Більшість продакшн-додатків на Next.js в підсумку мають хоча б один rewrite. Це найпростіший спосіб тримати API-ключі на сервері й уникнути CORS, не чіпаючи конфігурацію бекенду.

Заголовки (headers). Додавай заголовки безпеки глобально або для конкретних маршрутів:

javascript
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.

Змінні середовища. Два способи відкрити змінні для клієнта:

javascript
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.

javascript
// Неправильно - DB_PASS потрапить у клієнтський JavaScript env: { DB_PASS: process.env.DB_PASS }

Для серверних даних звертайся до process.env.DB_PASS всередині server component. Префікс NEXT_PUBLIC_ існує тільки для дійсно публічних значень.

Wildcard-хостнейм у remotePatterns.

javascript
// Неправильно - 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-проксі

javascript
// 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-конфіг

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-образ мінімальним.

Просунутий: спільна конфігурація в монорепо

javascript
// 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 вказує на різні бекенди залежно від середовища.

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.

Дочитали статтю?