Voltar para o blog
Performance

Como Medir e Otimizar Core Web Vitals em Next.js

28 de agosto de 202510 min de leituraPor Equipe GenStack
Next.jsPerformanceSEO

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

  • ✅ Usar Next.js Image component
  • ✅ Formato WebP/AVIF
  • ✅ Priority para hero images
  • ✅ Lazy loading para imagens below fold
  • ✅ Dimensões explícitas (width/height)
  • Fontes

  • ✅ next/font com display: swap
  • ✅ Preload de fontes críticas
  • ✅ Subsetting (apenas caracteres necessários)
  • JavaScript

  • ✅ Code splitting com dynamic()
  • ✅ Tree shaking de dependências
  • ✅ Remover console.logs em produção
  • ✅ Minificação com SWC
  • CSS

  • ✅ CSS Modules ou Tailwind (CSS-in-JS é mais lento)
  • ✅ Critical CSS inline
  • ✅ Remover CSS não utilizado
  • Dados

  • ✅ Server Components quando possível
  • ✅ Streaming com Suspense
  • ✅ ISR para conteúdo semi-estático
  • ✅ CDN para assets estáticos
  • 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).

    Compartilhar:

    Precisa de ajuda com seu projeto?

    Entre em contato e vamos criar uma solução sob medida para você.

    Falar com especialista