Felipe Moacir

Partial Hydration: JavaScript Seletivo

HydrationPerformanceArchitecture
Partial Hydration: JavaScript Seletivo

Partial Hydration é a técnica de não hidratar a página inteira. Só as partes interativas.

O Problema da Full Hydration

React tradicional:

  1. Servidor renderiza todo o HTML
  2. Cliente baixa todo o JavaScript
  3. Cliente re-executa todo o React para "hidratar"

Mesmo componentes 100% estáticos (texto, imagens) são hidratados.

Exemplo

// 95% estático, 5% interativo
export default function BlogPost() {
  return (
    <article>
      <h1>Título do Post</h1>
      <p>Parágrafo longo...</p>
      <p>Mais texto...</p>
      <p>Ainda mais texto...</p>
      
      <LikeButton /> {/* Só isso é interativo! */}
    </article>
  );
}

React hidrata tudo, incluindo <h1>, <p>, etc., que nunca mudam.

Partial Hydration: Só O Necessário

Com Partial Hydration, hidratamos apenas <LikeButton>.

Astro Islands

Astro é o pioneiro. Componentes são estáticos por padrão:

---
// BlogPost.astro
import LikeButton from './LikeButton.jsx';
---

<article>
  <h1>Título do Post</h1>
  <p>Texto estático...</p>
  
  <!-- Hidrata APENAS quando visível -->
  <LikeButton client:visible />
</article>

Diretivas:

  • client:load - Hidrata no page load
  • client:idle - Hidrata quando browser idle
  • client:visible - Hidrata quando entra na viewport
  • client:only - Só no cliente (sem SSR)

Resultado

Página de blog Astro:

  • JavaScript: 5KB (só LikeButton)

Mesma página React:

  • JavaScript: 200KB (React + todos componentes)

40x menos JavaScript.

Next.js Server Components

No App Router, componentes são server-only por padrão:

// app/post/page.jsx (Server Component)
import LikeButton from './LikeButton'; // Client Component

export default async function Post() {
  const post = await getPost();
  
  return (
    <article>
      <h1>{post.title}</h1>
      <p>{post.content}</p>
      
      <LikeButton postId={post.id} /> {/* Só isso hidrata */}
    </article>
  );
}

// LikeButton.jsx (Client Component)
'use client';

export default function LikeButton({ postId }) {
  const [likes, setLikes] = useState(0);
  
  return <button onClick={() => setLikes(likes + 1)}>❤️ {likes}</button>;
}

Server Components não enviam JavaScript. Só o <LikeButton> é hidratado.

Qwik: Zero Hydration

Qwik vai além: não há hydration. Só lazy-load quando interagir.

import { component$, useSignal } from '@builder.io/qwik';

export const Counter = component$(() => {
  const count = useSignal(0);
  
  return (
    <button onClick$={() => count.value++}>
      Clicks: {count.value}
    </button>
  );
});

Quando você clica:

  1. Qwik baixa o handler (onClick$)
  2. Executa
  3. Atualiza o DOM

Não há hydration prévia. JavaScript total inicial: ~1KB.

Performance: Comparação Real

Landing page com hero, features, testimonials, footer + 1 botão de newsletter:

React (Full Hydration)

  • JavaScript: 180KB
  • TTI: 2.3s

Astro (Partial Hydration)

  • JavaScript: 12KB (só botão newsletter)
  • TTI: 0.4s

Qwik (Zero Hydration)

  • JavaScript: 1KB
  • TTI: 0.05s

180x menos JavaScript com Qwik vs React.

Trade-offs

Astro/Next.js Server Components

✅ Reduz drasticamente JavaScript
✅ Fácil adotar (componentes isolados)

❌ Arquitetura mais complexa (client/server boundary)

Qwik

✅ Performance insana
✅ Escala para apps gigantes

❌ Ecossistema pequeno
❌ Curva de aprendizado alta

Quando Usar?

Partial Hydration (Astro, Next.js SC)

✅ Sites de conteúdo (blogs, marketing)
✅ E-commerce (produtos estáticos + cart interativo)
✅ Dashboards com sections estáticas

Zero Hydration (Qwik)

✅ Apps que precisam performance extrema
✅ Mobile-first (redes lentas)
✅ Disposto a aprender novo paradigma

Implementação: Next.js Server Components

// app/dashboard/page.jsx (Server)
import UserInfo from './UserInfo'; // Server
import Chart from './Chart'; // Client

export default async function Dashboard() {
  const user = await getUser();
  const stats = await getStats(); // Fetch no servidor
  
  return (
    <div>
      <UserInfo user={user} /> {/* Estático, 0KB JS */}
      
      <Chart data={stats} /> {/* Interativo, 50KB JS */}
    </div>
  );
}

// Chart.jsx (Client)
'use client';
import { LineChart } from 'recharts';

export default function Chart({ data }) {
  return <LineChart data={data} />; // Hidrata
}

Conclusão: Partial Hydration é o futuro. Full hydration é desperdício de recursos.