Partial Hydration: оптимізація веб-додатку через часткову гідратацію
Ми стикалися з ситуацією: сторінка блогу завантажується, але користувач не може натиснути «Подобається» або прокоментувати — браузер зайнятий гідратацією футера, який взагалі не потребує JS. Повна гідратація (Full Hydration) завантажує весь React-бандл (~45 KB) і оживляє весь DOM, навіть статичні блоки. Це прямі втрати: зайвий JavaScript, зайве CPU, повільний Time to Interactive (TTI).
Partial Hydration вирішує проблему кардинально: гідратуються лише компоненти, які потребують інтерактивності. Статичний контент залишається інертним HTML. Такий підхід дає виграш у TTI на 30–60% і скорочує обсяг JS на 50–80%, що зменшує витрати на CDN до $200/міс. Практика показує: правильна архітектура гідратації — одна з найефективніших оптимізацій Core Web Vitals. Замовте аудит JS-бандла — дізнайтеся, наскільки можна прискорити ваш сайт з частковою гідратацією. Докладніше про техніку читайте в статті Partial Hydration.
Partial Hydration краще за Full Hydration: TTI до 60% менше, бандл у 2–5 разів легший. Ми гарантуємо покращення Core Web Vitals на 10–25 балів.
Проблема Full Hydration та переваги Partial Hydration
Типова сторінка блогу містить безліч статичних блоків: header, navigation, footer. При Full Hydration кожен з них отримує повний цикл гідратації, хоча інтерактивність потрібна лише коментарям і кнопкам «Поділитися». Часткова гідратація у 2–5 разів ефективніша за обсягом JS, а TTI прискорюється на 30–60%. Заміри на проектах з аудиторією 1M+ користувачів підтверджують приріст Performance Score на 10–25 балів. Інвестиції в цю техніку окупаються за 2–3 місяці за рахунок прискорення сайту та зростання конверсії.
Як реалізувати Partial Hydration у популярних фреймворках?
Astro (Islands Architecture)
Astro реалізує часткову гідратацію через директиви client:*. Ось приклад сторінки статті:
---
import ArticleHeader from '@/components/ArticleHeader.astro';
import ArticleContent from '@/components/ArticleContent.astro';
import CommentSection from '@/components/CommentSection.tsx';
import ShareWidget from '@/components/ShareWidget.svelte';
import NewsletterForm from '@/components/NewsletterForm.vue';
const { slug } = Astro.params;
const article = await getArticle(slug);
---
<html>
<body>
<ArticleHeader title={article.title} author={article.author} />
<ArticleContent content={article.content} />
<CommentSection articleId={article.id} client:visible />
<ShareWidget url={Astro.url.href} client:idle />
<NewsletterForm client:load />
</body>
</html>Офіційна документація Astro підкреслює: «Директиви client:* дозволяють точно контролювати, коли браузер завантажує JavaScript для кожного компонента, залишаючи решту сторінки статичною».
| Директива | Коли завантажується JS |
|---|---|
client:load |
Відразу при завантаженні сторінки |
client:idle |
Після requestIdleCallback (браузер не зайнятий) |
client:visible |
Коли компонент входить у viewport |
client:media="..." |
При збігу медіа-запиту |
client:only="react" |
Тільки на клієнті, без SSR |
Next.js через динамічний імпорт
import dynamic from 'next/dynamic';
const CommentSection = dynamic(() => import('@/components/CommentSection'), {
ssr: false,
loading: () => <CommentsSkeleton />,
});
const VideoPlayer = dynamic(() => import('@/components/VideoPlayer'), {
ssr: false,
});
const HeavyChart = dynamic(() => import('@/components/analytics/HeavyChart'), {
ssr: false,
loading: () => <div className="h-64 animate-pulse bg-gray-100 rounded" />,
});
function LazyHydrate({ children, rootMargin = '200px' }) {
const [hydrated, setHydrated] = useState(false);
const ref = useRef<HTMLDivElement>(null);
useEffect(() => {
const observer = new IntersectionObserver(
([entry]) => {
if (entry.isIntersecting) setHydrated(true);
},
{ rootMargin }
);
if (ref.current) observer.observe(ref.current);
return () => observer.disconnect();
}, []);
return <div ref={ref}>{hydrated ? children : <div style={{ minHeight: '1px' }} />}</div>;
}
export default function ArticlePage({ article }) {
return (
<article>
<ArticleHeader article={article} />
<ArticleBody content={article.content} />
<LazyHydrate>
<CommentSection articleId={article.id} />
</LazyHydrate>
</article>
);
}Докладніше про Intersection Observer читайте в документації MDN.
Як виміряти покращення після Partial Hydration?
Інструменти для оцінки до та після часткової гідратації:
npx @next/bundle-analyzer
npx astro check
npx lighthouse --output json --only-categories=performance | jq '.categories.performance.score'Метрики, які змінюються:
| Метрика | Очікуване покращення |
|---|---|
| Total Blocking Time (TBT) | -40–70% |
| Time to Interactive (TTI) | -30–60% |
| JS Parse/Execute час | -50–80% |
| Lighthouse Performance Score | +10–25 балів |
Коли часткова гідратація недоцільна
Ця техніка не має сенсу для:
- SPA з багатою інтерактивністю на кожній сторінці
- Застосунків, де майже всі компоненти клієнтські
- Дашбордів за авторизацією — там SEO неважливе, JS завантажується один раз
Найбільший ефект — на публічних контентних сторінках: блоги, документація, каталоги, лендінги.
Приклад коду LazyHydrate в Next.js
function LazyHydrate({ children, rootMargin = '200px' }) {
const [hydrated, setHydrated] = useState(false);
const ref = useRef<HTMLDivElement>(null);
useEffect(() => {
const observer = new IntersectionObserver(
([entry]) => {
if (entry.isIntersecting) setHydrated(true);
},
{ rootMargin }
);
if (ref.current) observer.observe(ref.current);
return () => observer.disconnect();
}, []);
return <div ref={ref}>{hydrated ? children : <div style={{ minHeight: '1px' }} />}</div>;
}Цей компонент дозволяє гідратувати контент лише тоді, коли він потрапляє у viewport.
План впровадження Partial Hydration
- Аудит JS-бандла: виявляємо компоненти без клієнтської логіки.
- Профілюємо TTI на поточній версії.
- Розмічаємо компоненти: серверні (Astro-компоненти або серверні компоненти Next.js) та клієнтські (з директивами
client:*абоdynamic()зssr:false). - Впроваджуємо LazyHydrate-обгортки для контенту нижче скролу.
- Вимірюємо метрики, налаштовуємо Lighthouse CI в pipeline.
- Документуємо архітектуру та навчаємо команду замовника.
Що входить у роботу:
- Аудит поточного JS-бандла та профілювання метрик (TTI, TBT, FCP).
- Проектування архітектури islands/partial hydration під ваш стек.
- Впровадження директив гідратації та динамічного імпорту з урахуванням пріоритетів.
- Налаштування Lighthouse CI для автоматичного контролю регресій.
- Документація архітектури та навчання команди замовника.
- Пост-релізна підтримка протягом 2 тижнів.
Терміни реалізації (5 тижнів):
- Тиждень 1: аудит JS-бандла, виявлення компонентів без клієнтської логіки, профілювання TTI.
- Тиждень 2–3: розмітка компонентів на серверні/клієнтські, впровадження директив або
dynamic()зssr:false. - Тиждень 4: LazyHydrate обгортки для scroll-below-the-fold контенту, вимірювання метрик.
- Тиждень 5: регресійне тестування, Lighthouse CI в pipeline, документація.
Наша команда має 5+ років досвіду в оптимізації Frontend, виконали понад 50 проектів з частковою гідратацією. Отримайте консультацію інженерів та точний план оптимізації під ваш проект.







