Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Налаштування Next.js (next.config.js)». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**next.config.js** - файл конфігурації, який Next.js читає один раз при запуску для керування оптимізацією зображень, URL-rewrite, HTTP-заголовками та форматом деплою. ```javascript const nextConfig = { images: { remotePatterns: [{ protocol: 'https', hostname: 'cdn.example.com' }] }, output: 'standalone', }; module.exports = nextConfig; ``` **Ключове:** зміни вимагають перезапуску dev-сервера; значення фіксуються під час збірки, а не при кожному запиті.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення**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` вказує на різні бекенди залежно від середовища.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.