Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Що таке Next.js і чому його варто використовувати». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**Next.js** - це фреймворк для React, який додає серверний рендеринг, маршрутизацію на основі файлів і вбудовану оптимізацію. ```javascript // Server Component у Next.js - дані отримуються на сервері, повний HTML іде в браузер export default async function BlogPost({ params }) { const post = await fetch(`https://api.example.com/posts/${params.slug}`) .then(r => r.json()); return <article><h1>{post.title}</h1></article>; } // Google і користувачі бачать справжній контент з першого запиту ``` **Ключове:** React рендерить у браузері після завантаження JS. Next.js рендерить на сервері першим, тому пошукові системи бачать справжній HTML одразу, а користувачі отримують сторінки швидше.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення**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 | Погане без налаштування | Добре з коробки | | Перше завантаження | Чекає на JS | HTML + 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 щоб побачити свої дані.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.