Skip to main content

Що таке Next.js і чому його варто використовувати

Next.js - це фреймворк для React, який додає серверний рендеринг, маршрутизацію на основі файлів і вбудовану оптимізацію, щоб ти не налаштовував усе це вручну.

Теорія

TL;DR

  • React рендерить у браузері після завантаження JS; Next.js рендерить на сервері першим, відправляє HTML, потім гідратує через React
  • Структура файлів у app/ стає маршрутами автоматично, без React Router
  • SSR, SSG, ISR і CSR доступні для кожної сторінки окремо
  • Next.js для блогів, магазинів і маркетингових сайтів де важливе SEO; чистий React для дашбордів і внутрішніх інструментів

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

Різниця одразу видна в тому, як працює маршрутизація (routing):

javascript
// Чистий React - маршрутизацію налаштовуєш вручну import { BrowserRouter, Routes, Route } from 'react-router-dom'; export default function App() { return ( <BrowserRouter> <Routes> <Route path="/" element={<Home />} /> <Route path="/about" element={<About />} /> </Routes> </BrowserRouter> ); } // Next.js - структура файлів і є маршрутизація // app/page.js -> / // app/about/page.js -> /about // Жодного Router. Жодної конфігурації.

Файл є - маршрут є.

Ключова різниця

React - це бібліотека для UI. Вона рендерить компоненти в браузері після завантаження JavaScript, тому пошукові боти бачать порожній <div> поки JS не виконається. Next.js робить навпаки: сервер запускає твій компонент, генерує справжній HTML і відправляє його в браузер. Користувач бачить контент відразу. Потім у фоні завантажується React і гідратує (hydrates) сторінку - підключає обробники подій і робить її інтерактивною. Ті ж компоненти, ті ж хуки, але перший paint відбувається на сервері, а не в браузері.

Стратегії рендерингу

Next.js дає чотири режими рендерингу, і ти обираєш для кожної сторінки окремо:

  • SSR - сервер рендерить при кожному запиті. Підходить для персоналізованого контенту або даних, що змінюються постійно.
  • SSG - сторінка будується один раз при деплої, роздається з CDN. Найшвидший варіант для контенту, що рідко змінюється.
  • ISR - статична сторінка, яка регенерує в фоні за розкладом. CDN-швидкість плюс свіжіші дані.
  • CSR - звичайний React на клієнті. Використовуй коли сторінці не потрібне SEO і вона повністю інтерактивна.
javascript
// SSR: cache: 'no-store' - кожен запит повертає свіжий рендер з сервера export default async function Page() { const data = await fetch('https://api.example.com/posts', { cache: 'no-store' }); const posts = await data.json(); return <PostList posts={posts} />; } // ISR: регенерує раз на годину, в проміжку роздає закешований HTML export default async function Page() { const data = await fetch('https://api.example.com/posts', { next: { revalidate: 3600 } }); const posts = await data.json(); return <PostList posts={posts} />; }

Коли використовувати

Next.js підходить коли:

  • SEO важливе: блоги, магазини, маркетингові сторінки
  • Швидкість першого завантаження є пріоритетом
  • Потрібна серверна авторизація або персоналізація
  • Хочеш маршрутизацію, API routes та оптимізацію зображень вже готовими

Чистий React підходить краще коли:

  • Будуєш адмін-дашборд або внутрішній інструмент, який Google не індексує
  • Весь контент генерується на клієнті (як Figma або real-time редактор)
  • Робиш прототип і хочеш найпростіше налаштування

Таблиця порівняння

АспектReact (SPA)Next.js
МаршрутизаціяВручну (react-router)На основі файлів, автоматична
Початковий HTMLПорожній <div>Повний вміст сторінки
SEOПогане без налаштуванняДобре з коробки
Перше завантаженняЧекає на JSHTML + CSS спочатку
Отримання данихuseEffect на клієнтіServer Components, fetch на сервері
APIНемаєRoute Handlers вбудовані
ДеплойСтатичний хостингNode.js сервер або serverless
Для чогоДашборди, внутрішні інструментиБлоги, магазини, маркетинг

Як це працює всередині

Коли ти запитуєш сторінку Next.js, сервер запускає твій компонент і генерує HTML. Цей HTML іде в браузер одразу. Користувач бачить контент ще до того, як будь-який JavaScript запустився. Потім React завантажується і гідратує сторінку, підключаючи обробники подій.

Для статичних сторінок (SSG/ISR) цей HTML генерується заздалегідь і роздається з CDN. Сервер не задіяний на кожному запиті, тому статичні сторінки завантажуються швидше за SSR.

Server Components - це дефолт в App Router (Next.js 13+). Вони виконуються тільки на сервері, тому можна напряму читати з бази даних без ризику розкрити ключі доступу в браузері. Коли компоненту потрібна інтерактивність - useState або onClick - додаєш 'use client' вгорі файлу.

javascript
// Server Component (за замовчуванням) - виконується тільки на сервері export default async function UsersPage() { const users = await db.user.findMany(); // пряме звернення до БД, без витоку ключів return ( <ul> {users.map(user => ( <li key={user.id}>{user.name}</li> ))} </ul> ); } // Client Component - потрібен 'use client' для хуків і обробників подій 'use client'; import { useState } from 'react'; export default function Counter() { const [count, setCount] = useState(0); return ( <button onClick={() => setCount(count + 1)}> Кліки: {count} </button> ); }

Типові помилки

1. Fetch через useEffect замість async Server Component

Сенс Server Components у тому, що дані отримуються на сервері. Якщо ти використовуєш useEffect, ти повертаєшся до клієнтського рендерингу: порожній HTML іде спочатку, дані завантажуються після запуску JS.

javascript
// Неправильно - зводить нанівець серверний рендеринг export default function Page() { const [data, setData] = useState(null); useEffect(() => { fetch('/api/data').then(r => r.json()).then(setData); }, []); return <div>{data?.name}</div>; // браузер отримує порожній HTML } // Правильно - fetch безпосередньо в async компоненті export default async function Page() { const data = await fetch('https://api.example.com/data').then(r => r.json()); return <div>{data.name}</div>; // сервер відправляє повний HTML }

2. Використання хуків у Server Component

Server Components виконуються на сервері. useState, useContext, обробники подій - там не працюють. Рішення одне: 'use client' вгорі файлу.

3. Розбіжність при гідратації (hydration mismatch)

Якщо сервер рендерить HTML, що відрізняється від клієнтського, React не може правильно підключити обробники подій. Часта причина - new Date() безпосередньо в рендері.

javascript
// Неправильно - сервер рендерить '22:20:18', клієнт рендерить '22:20:25' export default function Time() { return <div>{new Date().toLocaleTimeString()}</div>; } // Правильно - час рендериться тільки на клієнті через useEffect 'use client'; import { useEffect, useState } from 'react'; export default function Time() { const [time, setTime] = useState(''); useEffect(() => setTime(new Date().toLocaleTimeString()), []); return <div>{time}</div>; }

4. Секретні API-ключі у файлах Server Components

Server Components виконуються на сервері, але код потрапляє в браузер через React Server Component payload. Секретні ключі видно в мережевих запитах. Тримай їх у .env.local і використовуй тільки в API routes або Server Actions, а не в компонентах.

5. Повне перебудовування сайту при кожному деплої замість ISR

10 000 сторінок товарів - це хвилини збірки при кожному деплої. Використовуй revalidate: перший запит генерує і кешує HTML, наступний проміжок часу всі запити б'ються в кеш миттєво, після інтервалу Next.js регенерує в фоні поки стара сторінка ще роздається.

Де зустрічається в реальних проектах

  • TikTok, Hulu, Nike: маркетингові сайти та магазини де важливе SEO
  • Stripe, GitHub: сайти з документацією
  • Shopify Hydrogen: commerce-фреймворк побудований на Next.js
  • Notion, Linear: Next.js для публічних сторінок, чистий React для внутрішніх інструментів
  • Vercel: вся їхня платформа на Next.js, Server Components для дашборду

Додаткові питання

Q: Яка різниця між Server Components і SSR?
A: SSR - це патерн деплою де React рендерить HTML на Node.js сервері при кожному запиті. Server Components - це фіча React 18, вони виконуються на сервері і стрімлять JSX на клієнт. Next.js використовує обидва: Server Components за замовчуванням, SSR для динамічних сторінок.

Q: Якщо Next.js рендерить на сервері, як обробляти real-time оновлення?
A: Server Components рендерять один раз і повертають статичний HTML. Для real-time даних використовуй Client Components з useEffect або WebSockets. Для нечастих оновлень Server Actions з revalidatePath() запускають свіжий рендер конкретних сторінок.

Q: Коли обирати SSG замість SSR?
A: SSG будує сторінки при деплої і роздає їх з CDN - миттєво, без витрат сервера на кожен запит. SSR рендерить при кожному запиті, що повільніше і дорожче. SSG для контенту, що рідко змінюється (пости, сторінки товарів). SSR для персоналізованого контенту або даних що змінюються постійно.

Q: Що відбувається якщо в Server Component є повільний запит до БД?
A: Сторінка чекає поки запит завершиться перш ніж відправити будь-який HTML в браузер. Обертай повільні компоненти в <Suspense> - решта сторінки рендериться одразу, а повільний компонент стрімиться пізніше коли дані готові.

Q: (Senior рівень) Як побудувати сайт де частина сторінок статична, частина SSR, а частина CSR?
A: revalidate (ISR) для переважно статичних сторінок типу лістингу товарів. SSR (cache: 'no-store') для сторінок з персоналізацією або авторизацією. Client Components для повністю інтерактивних секцій. Статика найшвидша але може застарівати. SSR завжди свіже але з затримкою сервера. CSR інтерактивний але без SEO. Правильний розподіл залежить від того як часто змінюється контент і чи потрібно Google його індексувати.

Приклади

Отримання даних на сервері для сторінки блогу

javascript
// app/blog/[slug]/page.js // Виконується на сервері. params.slug береться з сегмента URL. export default async function BlogPost({ params }) { const post = await fetch( `https://api.example.com/posts/${params.slug}` ).then(r => r.json()); // Сервер генерує повністю заповнений HTML перед відправкою в браузер. // Google бачить весь контент статті одразу. return ( <article> <h1>{post.title}</h1> <p>{post.content}</p> </article> ); }

URL /blog/next-js-guide встановлює params.slug у "next-js-guide". Fetch виконується на сервері. HTML що приходить в браузер вже містить заголовок і текст, а не порожні місця.

ISR для сторінки товару з 10 000 продуктів

javascript
// app/products/[id]/page.js export const revalidate = 3600; // регенерує раз на годину export default async function Product({ params }) { const product = await fetch( `https://api.example.com/products/${params.id}`, { next: { revalidate: 3600 } } ).then(r => r.json()); return ( <div> <h1>{product.name}</h1> <p>${product.price}</p> </div> ); }

Перший запит генерує і кешує HTML. Наступну годину всі запити б'ються в кеш миттєво. Після години Next.js регенерує в фоні поки стара сторінка ще роздається. Зміна ціни видима в межах години без торкання pipeline деплою.

Комбінація Server і Client Components

javascript
// app/dashboard/page.js - Server Component отримує дані import StatsChart from './StatsChart'; export default async function Dashboard() { const stats = await fetch('https://api.example.com/stats', { cache: 'no-store' }).then(r => r.json()); // Передаємо дані в Client Component, який відповідає за інтерактивність return ( <main> <h1>Дашборд</h1> <StatsChart data={stats} /> </main> ); }
javascript
// app/dashboard/StatsChart.js - Client Component керує станом 'use client'; import { useState } from 'react'; export default function StatsChart({ data }) { const [view, setView] = useState('weekly'); return ( <div> <button onClick={() => setView('weekly')}>Тиждень</button> <button onClick={() => setView('monthly')}>Місяць</button> <p>Дані за {view === 'weekly' ? 'тиждень' : 'місяць'}: {data[view]}</p> </div> ); }

Сервер робить важкий запит до даних, клієнт керує станом без зайвих мережевих запитів. Я використовував цей патерн на реальних дашбордах - початкове завантаження залишається швидким, бо користувач не чекає на JS щоб побачити свої дані.

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

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

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

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