Skip to main content
Практика завдань

Стрімінг та завантаження інтерфейсу в Next.js

Стрімінг дозволяє надсилати HTML клієнту частинами, коли вони готові. Замість того, щоб чекати завершення всіх запитів, користувач відразу бачить частину сторінки, поки решта завантажується.

Проблема без стрімінгу

Без стрімінгу весь HTML генерується перед відправкою:

Всі дані завантажуються на сервері

Запит до бази даних для проблем, API виклик для статистики, отримання профілю. Все послідовно або паралельно, але HTML надсилається лише тоді, коли все готове.

Користувач чекає

Білий екран, поки сервер збирає всю сторінку. TTFB може становити кілька секунд.

Як стрімінг вирішує цю проблему

Завдяки стрімінгу Next.js надсилає частини сторінки, коли вони готові:

Миттєва відповідь

HTML з макетом, навігацією та скелетами надсилається негайно.

Прогресивне завантаження

Дані завантажуються прогресивно. Кожен блок замінює свій скелет.

loading.tsx

Найпростіший спосіб додати UI завантаження. Next.js автоматично обгортає page.tsx у React Suspense з резервним варіантом з loading.tsx:

tsx
// app/problems/loading.tsx export default function Loading() { return ( <div className="space-y-4"> {[...Array(5)].map((_, i) => ( <div key={i} className="h-16 bg-neutral-200 animate-pulse rounded-lg" /> ))} </div> ) }

Користувач бачить скелет негайно, поки контент з'являється в міру завантаження даних.

Suspense для детального контролю

Для більш точного контролю використовуйте Suspense безпосередньо:

tsx
// app/dashboard/page.tsx import { Suspense } from 'react' export default function DashboardPage() { return ( <div> <h1>Панель управління</h1> <Suspense fallback={<StatsSkeleton />}> <UserStats /> </Suspense> <Suspense fallback={<ActivitySkeleton />}> <RecentActivity /> </Suspense> <Suspense fallback={<ProgressSkeleton />}> <RoadmapProgress /> </Suspense> </div> ) }

Кожна межа Suspense стрімить незалежно. UserStats може з'явитися раніше, ніж RecentActivity.

Як це працює під капотом

tsx
// 1. Сервер надсилає HTML з резервними варіантами <div> <h1>Панель управління</h1> <div id="stats"><!-- StatsSkeleton --></div> <div id="activity"><!-- ActivitySkeleton --></div> </div> // 2. Коли дані готові, сервер надсилає <script> // який замінює резервний варіант на реальний контент <script> $RC("stats", "<div>Вирішено проблем: 42</div>") </script>

Браузер отримує потік даних через одне HTTP-з'єднання.

Шаблон: Асинхронний серверний компонент

Suspense працює з асинхронними серверними компонентами:

tsx
// Цей компонент буде стрімитися async function UserStats() { const stats = await db.user.getStats(userId) // Запит може зайняти 2 секунди // Користувач бачить скелет, поки дані завантажуються return ( <div> <p>Вирішено проблем: {stats.problemsSolved}</p> <p>Прочитані статті: {stats.docsRead}</p> </div> ) }

loading.tsx проти Suspense

loading.tsxSuspense
ОбластьВся сторінка (page.tsx)Конкретний компонент
ГранулярністьОдин резервний варіант на сторінкуКілька незалежних
НалаштуванняАвтоматичнеРучне

Практична порада:

Використовуйте loading.tsx для простих сторінок з одним блоком даних. Для панелей управління та складних сторінок використовуйте Suspense для кожного блоку окремо, щоб контент з'являвся в міру готовності.

Корисні ресурси

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

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

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

Дочитали статтю?
Практика завдань