Core Web Vitals são métricas essenciais que o Google usa para avaliar a experiência do usuário. Vamos otimizá-las em Next.js.
O Que São Core Web Vitals?
Três métricas principais:
1. LCP - Largest Contentful Paint
**O que é:** Tempo até o maior elemento visível carregar
**Meta:** < 2.5 segundos
**Impacto:** Percepção de velocidade
2. FID - First Input Delay (ou INP)
**O que é:** Tempo de resposta à primeira interação
**Meta:** < 100ms (FID) ou < 200ms (INP)
**Impacto:** Responsividade
3. CLS - Cumulative Layout Shift
**O que é:** Estabilidade visual durante o carregamento
**Meta:** < 0.1
**Impacto:** Frustração do usuário
Medindo Core Web Vitals
1. No Next.js (Código)
```typescript
// app/layout.tsx
import { Analytics } from '@vercel/analytics/react';
import { SpeedInsights } from '@vercel/speed-insights/next';
export default function RootLayout({ children }) {
return (
<html>
<body>
{children}
<Analytics />
<SpeedInsights />
</body>
</html>
);
}
```
2. Web Vitals API
```typescript
// lib/vitals.ts
import { onCLS, onFID, onLCP, onINP } from 'web-vitals';
export function reportWebVitals() {
onCLS(console.log);
onFID(console.log);
onLCP(console.log);
onINP(console.log);
}
// app/layout.tsx
'use client';
import { useEffect } from 'react';
import { reportWebVitals } from '@/lib/vitals';
export default function Layout({ children }) {
useEffect(() => {
reportWebVitals();
}, []);
return <>{children}</>;
}
```
3. Ferramentas Externas
- **PageSpeed Insights:** https://pagespeed.web.dev/
- **Lighthouse:** DevTools > Lighthouse
- **Search Console:** Relatório de Experiência
- **WebPageTest:** https://www.webpagetest.org/
Otimizando LCP
1. Otimização de Imagens
❌ Ruim:
```jsx
<img src="/hero.jpg" alt="Hero" />
```
✅ Bom:
```jsx
import Image from 'next/image';
<Image
src="/hero.jpg"
alt="Hero"
width={1200}
height={600}
priority // Para hero images
quality={90}
sizes="(max-width: 768px) 100vw, 1200px"
/>
```
2. Font Optimization
❌ Ruim:
```html
<link href="https://fonts.googleapis.com/css2?family=Inter" rel="stylesheet">
```
✅ Bom:
```typescript
// app/layout.tsx
import { Inter } from 'next/font/google';
const inter = Inter({
subsets: ['latin'],
display: 'swap',
variable: '--font-inter',
});
export default function RootLayout({ children }) {
return (
<html className={inter.variable}>
<body>{children}</body>
</html>
);
}
```
3. Preload Critical Resources
```typescript
// app/layout.tsx
export default function RootLayout() {
return (
<html>
<head>
<link
rel="preload"
href="/hero.jpg"
as="image"
/>
<link
rel="preconnect"
href="https://api.example.com"
/>
</head>
<body>{children}</body>
</html>
);
}
```
4. Server Components (App Router)
```typescript
// app/page.tsx (Server Component por padrão)
export default async function HomePage() {
// Fetch no servidor - sem waterfall no cliente
const data = await fetch('https://api.example.com/data').then(r => r.json());
return (
<main>
<Hero data={data} />
</main>
);
}
```
Otimizando FID/INP
1. Code Splitting
✅ Dynamic Imports:
```typescript
import dynamic from 'next/dynamic';
// Componente pesado carregado sob demanda
const HeavyComponent = dynamic(() => import('./HeavyComponent'), {
loading: () => <Skeleton />,
ssr: false, // Desabilita SSR se não for crítico
});
export default function Page() {
return (
<div>
<HeavyComponent />
</div>
);
}
```
2. Debounce e Throttle
```typescript
import { useCallback } from 'react';
import { debounce } from 'lodash';
function SearchInput() {
const handleSearch = useCallback(
debounce((value: string) => {
// Busca pesada
fetch(`/api/search?q=${value}`);
}, 300),
[]
);
return (
<input
onChange={(e) => handleSearch(e.target.value)}
placeholder="Buscar..."
/>
);
}
```
3. Web Workers para Tasks Pesadas
```typescript
// workers/heavy-calculation.ts
self.addEventListener('message', (e) => {
const result = performHeavyCalculation(e.data);
self.postMessage(result);
});
// components/Calculator.tsx
import { useEffect, useState } from 'react';
function Calculator() {
const [result, setResult] = useState(null);
useEffect(() => {
const worker = new Worker(
new URL('../workers/heavy-calculation.ts', import.meta.url)
);
worker.postMessage({ data: complexData });
worker.onmessage = (e) => {
setResult(e.data);
};
return () => worker.terminate();
}, []);
return <div>{result}</div>;
}
```
Otimizando CLS
1. Reserve Espaço para Imagens
❌ Ruim:
```jsx
<img src="/photo.jpg" /> // Causa layout shift
```
✅ Bom:
```jsx
<Image
src="/photo.jpg"
width={800}
height={600}
alt="Photo"
/>
```
2. Reserve Espaço para Ads e Embeds
```css
.ad-container {
min-height: 250px; /* Reserve espaço */
background: #f0f0f0;
}
```
3. Evite Inserir Conteúdo Acima do Existente
❌ Ruim:
```jsx
// Banner aparece depois, empurrando conteúdo
{showBanner && <Banner />}
<Content />
```
✅ Bom:
```jsx
// Reserve espaço sempre
<div className="min-h-[80px]">
{showBanner && <Banner />}
</div>
<Content />
```
4. Use font-display: swap
```typescript
const inter = Inter({
subsets: ['latin'],
display: 'swap', // Evita invisible text
});
```
Configuração Next.js para Performance
next.config.js
```javascript
/** @type {import('next').NextConfig} */
const nextConfig = {
// Otimização de imagens
images: {
formats: ['image/avif', 'image/webp'],
deviceSizes: [640, 750, 828, 1080, 1200, 1920],
imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
},
// Compressão
compress: true,
// SWC minification (mais rápido)
swcMinify: true,
// Experimental
experimental: {
optimizeCss: true,
optimizePackageImports: ['lucide-react', 'date-fns'],
},
};
module.exports = nextConfig;
```
Checklist de Performance
Imagens
Fontes
JavaScript
CSS
Dados
Monitoramento Contínuo
1. Vercel Analytics
```bash
npm install @vercel/analytics @vercel/speed-insights
```
2. Google Analytics 4 com Web Vitals
```typescript
// lib/gtag.ts
export const sendWebVitals = (metric: any) => {
window.gtag('event', metric.name, {
value: Math.round(metric.name === 'CLS' ? metric.value * 1000 : metric.value),
event_category: 'Web Vitals',
non_interaction: true,
});
};
```
3. Alertas Automatizados
Configure alertas quando métricas degradarem:
- LCP > 2.5s
- CLS > 0.1
- INP > 200ms
Conclusão
Core Web Vitals não são apenas números - são indicadores diretos da experiência do usuário e impactam:
- **SEO:** Ranking no Google
- **Conversão:** Usuários satisfeitos convertem mais
- **Retenção:** Sites lentos perdem visitantes
Próximos passos:
1. Meça suas métricas atuais
2. Priorize otimizações de maior impacto
3. Implemente melhorias incrementalmente
4. Monitore continuamente
5. Itere baseado em dados reais
Precisa de ajuda para otimizar? [Fale conosco](/contato).