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 проектів з частковою гідратацією. Отримайте консультацію інженерів та точний план оптимізації під ваш проект.







