Junior: питання на співбесіді з теми «Next.js, Inertia й маршрутизація»
Питання з реальних співбесід з відповідями: Laravel і PHP, бази даних, JavaScript і фронтенд, Git, Docker, API, безпека й архітектура. Тими самими темами, що й тести.
4 питання
Є три поширені архітектури, і вони відрізняються тим, хто відповідає за маршрутизацію й дані.
1. Окрема SPA + API Laravel (Vite + React Router, Laravel лише як JSON API, автентифікація через Sanctum):
- фронтенд і бекенд незалежні: окремі деплої, можна мати кілька клієнтів (веб, мобільний застосунок);
- треба самому будувати API, валідацію відповідей, обробку помилок, стан завантаження, маршрутизацію й автентифікацію (CORS, CSRF-cookie);
- без SSR сторінки погано індексуються - для публічного контенту це мінус.
2. Inertia.js (React-сторінки, але маршрути й контролери - Laravel):
return Inertia::render('Orders/Show', ['order' => $order->only('id', 'total', 'status')]);
export default function Show({ order }) { /* ... */ }
- немає окремого API: контролер передає дані як props сторінки, валідація, авторизація, редиректи, сесії - звичайний Laravel;
- переходи без перезавантаження, як у SPA;
- SSR можливий (окремий Node-процес), але не обов'язковий;
- один застосунок - простіше для невеликої команди. Офіційний стартовий набір Laravel для React побудований саме так.
3. Next.js + Laravel як API:
- React Server Components, потоковий рендер, кешування на рівні фреймворка, SSR/SSG з коробки - найкраще для SEO й публічних сайтів з великим трафіком;
- два бекенди: Node-сервер Next.js і Laravel. Автентифікація, кешування й деплой стають складнішими;
- частина логіки (серверні дії, маршрути) переїжджає з Laravel у Next.js - треба вирішити, де межа.
Як обирати:
| Ситуація | Вибір |
|---|---|
| адмінка, кабінет, внутрішній інструмент на Laravel | Inertia |
| кілька клієнтів одного API (веб + мобільний) | SPA + API |
| публічний контент-сайт з вимогами до SEO й швидкості | Next.js (або Inertia з SSR) |
| команда з окремими фронтенд- і бекенд-розробниками | SPA чи Next.js + API |
Головне питання: чи потрібен окремий API як продукт. Якщо ні - Inertia дає SPA-досвід без витрат на API.
React Router має три режими, і кожен наступний додає можливості поверх попереднього.
1. Декларативний - маршрути описано в JSX, лише зіставлення URL з компонентами й навігація:
import { BrowserRouter, Routes, Route, Link } from 'react-router';
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/posts/:id" element={<Post />} />
</Routes>
</BrowserRouter>
Хуки useParams, useNavigate, useLocation, компонент <Link>. Дані завантажує сам компонент.
2. Режим даних - маршрути описано об'єктами поза рендером, і до них додаються завантажувачі даних і дії:
const router = createBrowserRouter([
{ path: '/posts/:id', Component: Post, loader: ({ params }) => getPost(params.id) },
]);
<RouterProvider router={router} />
Дані завантажуються до рендеру сторінки, паралельно для вкладених маршрутів, - без «водоспаду» запитів у useEffect. Дії (action) обробляють форми, після чого дані сторінки перевантажуються автоматично.
3. Фреймворковий - режим даних плюс плагін Vite: типобезпечні модулі маршрутів, автоматичне розділення коду, SSR, статичний рендер чи SPA на вибір. По суті це наступник Remix.
Як обирати: документація радить фреймворковий режим тим, хто не має сильної думки, - він дає найбільше. Декларативний - для простих застосунків або поступового переходу; режим даних - коли потрібні завантажувачі, але власна збірка.
React Router 8 (червень 2026) - перший реліз за щорічним графіком мажорних версій:
- пакет
react-router-domвидалено - усі імпорти зreact-router(іreact-router/dom); - мінімальні версії: React 19.2.7+, Node 22.22+, Vite 7+; пакети тепер лише ESM;
- поведінка прапорців
future.v8_*стала типовою: middleware маршрутів увімкнено завжди, модулі маршрутів розділяються на частини за замовчуванням; - у
meta-функціях замість застарілогоdata-loaderData.
Якщо у v7 всі прапорці майбутньої версії вже були увімкнені, перехід на v8 мінімальний.
З Laravel React Router доречний в окремій SPA. В Inertia маршрутизація належить Laravel, і React Router не потрібен.
У App Router маршрути визначає структура папок у app/, а спеціальні файли всередині папки задають роль.
app/
├── layout.tsx # кореневий макет (обов'язковий, містить <html> і <body>)
├── page.tsx # /
├── blog/
│ ├── layout.tsx # макет для всіх сторінок /blog/*
│ ├── page.tsx # /blog
│ └── [slug]/
│ ├── page.tsx # /blog/:slug
│ ├── loading.tsx # стан завантаження
│ └── error.tsx # межа помилок
page.tsx - вміст маршруту. Без нього папка не стає доступним URL.
layout.tsx - спільна обгортка для сторінки й усіх вкладених маршрутів:
export default function BlogLayout({ children }: { children: React.ReactNode }) {
return (
<section>
<BlogSidebar />
{children}
</section>
);
}
Ключова властивість макетів: при переході між сторінками всередині макета він не перемонтовується - зберігає стан (відкрита бічна панель, введений пошук) і не рендериться заново. Перемальовується лише змінена частина.
Динамічні сегменти: [slug] - параметр; params у сторінці - Promise, його треба дочекатися:
export default async function Post({ params }: { params: Promise<{ slug: string }> }) {
const { slug } = await params;
const post = await getPost(slug);
return <article>{post.title}</article>;
}
Інші файли:
loading.tsx- автоматичний<Suspense>: запасний вигляд, поки сторінка завантажує дані;error.tsx- межа помилок сегмента (має бути клієнтським компонентом);not-found.tsx- дляnotFound();route.ts- обробник HTTP-запитів (API) замість сторінки.
Групи маршрутів (marketing) - папки в дужках не потрапляють в URL і дають змогу мати різні макети для частин сайту.
За замовчуванням усі ці компоненти - серверні: можна звертатися до бази чи API прямо в компоненті, а 'use client' додається лише там, де потрібна інтерактивність.
Next.js читає .env-файли й робить змінні доступними через process.env. Але де саме вони доступні, залежить від префікса.
Без префікса - лише на сервері:
DATABASE_URL=postgres://...
STRIPE_SECRET_KEY=sk_live_...
Доступні в серверних компонентах, обробниках маршрутів, серверних функціях. У браузері process.env.STRIPE_SECRET_KEY - undefined.
З префіксом NEXT_PUBLIC_ - у браузері теж:
NEXT_PUBLIC_ANALYTICS_ID=G-XXXX
NEXT_PUBLIC_API_URL=https://api.example.com
setupAnalytics(process.env.NEXT_PUBLIC_ANALYTICS_ID);
Як це працює: під час next build Next.js підставляє значення прямо в код JavaScript, що йде в браузер. Тобто:
- значення публічне - будь-хто побачить його в коді сторінки. Сюди можна класти лише те, що й так не секретно;
- значення фіксується на момент збирання. Змінити
NEXT_PUBLIC_API_URLна сервері після збірки - нічого не змінить у вже зібраних файлах. Один Docker-образ для staging і production з різнимиNEXT_PUBLIC_*не працюватиме як очікується; - динамічне звернення не підставляється:
process.env[name]чи деструктуризаціяconst { NEXT_PUBLIC_X } = process.envдадутьundefinedу браузері.
Як передати конфігурацію під час виконання, якщо образ один на всі середовища:
- читати змінні без префікса в серверному компоненті й передавати потрібні значення в клієнтські компоненти як props;
- або ендпойнт конфігурації, з якого клієнт отримує налаштування.
Порядок файлів: .env.local (не в Git, перекриває все), .env.development / .env.production, .env. Змінні оточення процесу мають пріоритет над файлами.
Пастки безпеки:
- випадково додати
NEXT_PUBLIC_до секрету - класичний витік ключа API; - серверний модуль з секретами, імпортований у клієнтський компонент, потрапляє в бандл - захист: пакет
server-onlyу таких модулях дає помилку збірки при імпорті з клієнта.
Аналог у Vite (Laravel-проєкти) - префікс VITE_ і import.meta.env, з тими самими правилами: усе з префіксом публічне й фіксується при збиранні.