Otimização do Core Web Vitals no Next.js: guia completo de LCP, FCP e CLS

No mês passado, assumi a otimização de desempenho de um projeto de e-commerce. A pontuação do Lighthouse ficava em torno de 65, e o LCP (Largest Contentful Paint) oscilava perto de 3,5 segundos. O responsável pelo negócio dizia que a taxa de abandono era alta e que a conversão não melhorava.
Depois de duas semanas otimizando sistematicamente o Core Web Vitals, levei a pontuação do Lighthouse a 95 e reduzi o LCP para 1,2 segundo. Mais importante: a taxa de conversão aumentou 28%. Otimização de desempenho não é apenas uma questão de indicadores técnicos; ela gera valor real para o negócio.
Segundo os dados mais recentes de 2025, apenas 47% dos sites atendem aos padrões de Core Web Vitals do Google. Um desempenho ruim pode causar perdas de 8% a 35% em receita, ranqueamento e conversão. Não é alarmismo, mas um custo comercial concreto.
Neste artigo, compartilho o que aprendi na prática ao otimizar o desempenho de projetos Next.js. Você vai aprender:
- Como otimizar os três indicadores principais: LCP, INP e CLS
- Mais de 10 exemplos de código que você pode copiar diretamente
- As cinco armadilhas de desempenho mais comuns
O que mudou no Core Web Vitals em 2025
Antes de começar a otimizar, precisamos entender as regras atuais. Muitos artigos ainda falam de FID (First Input Delay), mas esse indicador foi descontinuado em março de 2024.
Os três indicadores principais atuais
Hoje, o Google prioriza estes três indicadores:
-
LCP (Largest Contentful Paint) — maior renderização de conteúdo
- Meta: ≤ 2,5 segundos
- Mede o desempenho de carregamento
- Representa 25% da pontuação do Lighthouse
-
INP (Interaction to Next Paint) — interação até a próxima renderização
- Meta: ≤ 200 ms
- Mede a capacidade de resposta às interações
- Substituiu o FID em março de 2024
-
CLS (Cumulative Layout Shift) — mudança cumulativa de layout
- Meta: < 0,1
- Mede a estabilidade visual
O FCP (First Contentful Paint) deixou de ser um indicador principal, mas ainda é importante porque afeta a primeira impressão do usuário.
Por que acompanhar esses indicadores?
Talvez você já tenha aberto um site e, no momento de clicar em um botão, a página tenha se deslocado de repente, fazendo você clicar no lugar errado. Isso é um exemplo de CLS ruim.
Ou talvez você tenha acessado um site e ficado vários segundos olhando para uma tela em branco, pensando se a conexão havia caído. Esse é o efeito de um LCP lento.
Segundo dados da equipe do Chrome, os melhores sites têm LCP médio de cerca de 1.220 ms. Se o LCP do seu site ultrapassa 2,5 segundos, você já está atrás de grande parte dos concorrentes.
Otimização de LCP: exiba rapidamente o maior elemento de conteúdo
O LCP foi o indicador em que investi mais tempo e também aquele que mostrou o resultado mais evidente. A seguir, vamos otimizar esse ponto passo a passo.
Primeiro passo: identifique o elemento de LCP
Antes de otimizar, você precisa saber qual elemento está definindo o LCP. Em geral, ele é:
- A imagem principal da primeira tela
- A capa de um vídeo
- Um título grande ou um bloco extenso de texto
Como verificar rapidamente:
- Abra o Chrome DevTools com
F12 - Pressione
Ctrl+Shift+P— no Mac, useCmd+Shift+P - Digite “Show Rendering”
- Marque “Core Web Vitals”
- Atualize a página; o elemento de LCP será mostrado no canto superior direito
No projeto de e-commerce, descobri que o elemento de LCP era a imagem principal da página inicial: um JPG de 2 MB. Ali estava o problema.
Otimização de imagens: como usar next/image corretamente
Muita gente pensa que basta usar o componente Image do Next.js para resolver tudo. Eu também pensava assim no começo, mas o LCP continuava lento.
Definir a prioridade é o ponto decisivo
Para uma imagem de LCP, você precisa dizer explicitamente ao navegador que ela é importante e deve ser carregada primeiro. O Next.js oferece duas formas de fazer isso.
Next.js 13 a 15, as versões mais usadas:
import Image from 'next/image';
export default function Hero() {
return (
<Image
src="/hero-image.jpg"
width={1200}
height={630}
priority // Essencial: instrui o navegador a priorizar a imagem
fetchPriority="high" // Uma proteção adicional
alt="Imagem principal do produto"
/>
);
}
Next.js 16 ou superior, a versão mais recente:
No Next.js 16, a propriedade priority foi descontinuada. Use esta forma:
<Image
src="/hero-image.jpg"
width={1200}
height={630}
loading="eager" // Carrega imediatamente, sem lazy loading
fetchPriority="high" // Prioridade alta
alt="Imagem principal do produto"
/>
Você pode se perguntar por que definir duas propriedades. priority adiciona automaticamente uma tag de pré-carregamento, enquanto fetchPriority informa ao navegador a importância do recurso. As duas funcionam melhor em conjunto.
Compare com estas formas incorretas:
// ❌ Incorreto: a imagem será carregada sob demanda, deixando o LCP lento
<Image
src="/hero-image.jpg"
width={1200}
height={630}
alt="Imagem principal do produto"
/>
// ❌ Incorreto: faltam informações de dimensão
<Image
src="/hero-image.jpg"
priority
alt="Imagem principal do produto"
/>
A importância de declarar as dimensões
O componente Image do Next.js exige width e height. Não é uma dificuldade arbitrária: essas propriedades evitam CLS, ou seja, deslocamentos no layout.
Em um layout responsivo, você pode escrever assim:
// Use a propriedade fill para criar uma imagem responsiva
<div style={{ position: 'relative', width: '100%', aspectRatio: '16/9' }}>
<Image
src="/hero-image.jpg"
fill
priority
style={{ objectFit: 'cover' }}
alt="Imagem principal do produto"
/>
</div>
O contêiner pai precisa ter dimensões explícitas ou um aspect-ratio. Caso contrário, a imagem não sabe qual tamanho deve ocupar.
Otimização de fontes: incorporação automática com next/font
Fontes lentas também prejudicam o LCP, especialmente fontes com conjuntos de caracteres grandes, que podem ocupar vários megabytes. O Next.js 13 introduziu o next/font, que otimiza automaticamente o carregamento.
Usando Google Fonts:
// app/layout.jsx
import { Inter, Noto_Sans_SC } from 'next/font/google';
const inter = Inter({
subsets: ['latin'],
display: 'swap', // Evita o desaparecimento temporário do texto
});
const notoSansSC = Noto_Sans_SC({
subsets: ['chinese-simplified'],
weight: ['400', '700'],
display: 'swap',
});
export default function RootLayout({ children }) {
return (
<html lang="zh-CN" className={`${inter.className} ${notoSansSC.className}`}>
<body>{children}</body>
</html>
);
}
Usando uma fonte personalizada:
import localFont from 'next/font/local';
const myFont = localFont({
src: './my-font.woff2',
display: 'swap',
});
export default function Layout({ children }) {
return <div className={myFont.className}>{children}</div>;
}
O next/font aplica automaticamente estas otimizações:
- Incorpora o CSS das fontes e reduz o número de requisições
- Cria subconjuntos e carrega apenas os caracteres necessários
- Pré-carrega os arquivos de fonte
- Elimina deslocamentos de layout
Depois que migrei as fontes do projeto para next/font, o LCP caiu mais 0,3 segundo.
Reduza o tempo de resposta do servidor
Você já otimizou imagens e fontes, mas o LCP continua lento? O problema pode estar no tempo de resposta do servidor, ou TTFB.
Priorize a geração estática
O Next.js oferece várias formas de renderização. Da mais rápida para a mais lenta, a ordem é:
- SSG (Static Site Generation) — gera o HTML no build e é a opção mais rápida
- ISR (Incremental Static Regeneration) — regenera páginas estáticas sob demanda
- SSR (Server-Side Rendering) — gera o HTML a cada requisição e é mais lento
- CSR (Client-Side Rendering) — renderiza no cliente e costuma ter o LCP mais lento
Sempre que puder, use SSG:
// app/blog/[slug]/page.jsx
export async function generateStaticParams() {
const posts = await getPosts();
return posts.map((post) => ({
slug: post.slug,
}));
}
export default async function BlogPost({ params }) {
const post = await getPost(params.slug);
return <article>{/* ... */}</article>;
}
Se os dados precisam de atualização frequente, use ISR:
// app/products/[id]/page.jsx
export const revalidate = 3600; // Regenera uma vez por hora
export default async function ProductPage({ params }) {
const product = await getProduct(params.id);
return <div>{/* ... */}</div>;
}
Use uma CDN e uma Edge Network
Se você implanta o projeto na Vercel, os recursos estáticos são distribuídos automaticamente pela Edge Network global, reduzindo bastante o TTFB.
Em outras plataformas, você pode usar uma CDN como Cloudflare ou AWS CloudFront.
Evite cálculos complexos no servidor
Eu também já caí nessa armadilha. Em um projeto, fazia um processamento complexo no servidor que levava 500 ms a cada renderização e prejudicava diretamente o LCP.
Exemplo do que não fazer:
// ❌ Incorreto: calcula tudo em cada requisição
export default async function Page() {
const data = await fetchData();
const processed = heavyProcessing(data); // Leva 500 ms
return <div>{processed}</div>;
}
Forma correta:
Armazene o resultado em cache ou faça o cálculo durante o build:
// ✅ Correto: calcula durante o build
export async function generateStaticParams() {
const data = await fetchData();
const processed = heavyProcessing(data);
// Armazene o resultado no banco de dados ou em um arquivo
await saveProcessedData(processed);
}
export default async function Page() {
const processed = await getProcessedData(); // Apenas lê o resultado
return <div>{processed}</div>;
}
Otimização de CLS: elimine os saltos no layout
O CLS (Cumulative Layout Shift) é um dos indicadores mais incômodos. Quando a página muda de posição de repente, a experiência do usuário fica ruim. Para ser sincero, esse problema me incomodou por bastante tempo.
Reserve espaço para imagens e elementos de mídia
A causa mais comum de CLS é uma imagem que altera o layout depois de carregar. A solução é simples: informe antecipadamente as dimensões ao navegador.
O componente Image do Next.js cuida disso automaticamente:
// ✅ Correto: as dimensões evitam CLS
<Image
src="/product.jpg"
width={400}
height={300}
alt="Imagem do produto"
/>
E quando a imagem é carregada dinamicamente?
Imagine que você receba uma imagem de um CMS sem saber suas dimensões:
// ✅ Correto: use aspect-ratio para reservar espaço
<div style={{ position: 'relative', width: '100%', aspectRatio: '4/3' }}>
<Image
src={dynamicImageUrl}
fill
style={{ objectFit: 'cover' }}
alt="Imagem dinâmica"
/>
</div>
O mesmo vale para vídeos:
<video
width="1280"
height="720"
poster="/video-poster.jpg"
controls
>
<source src="/video.mp4" type="video/mp4" />
</video>
Reserve espaço para conteúdo dinâmico
Anúncios, barras de aviso e conteúdo incorporado, como cards do Twitter, podem causar CLS. Reserve espaço para eles com antecedência.
Contêiner de anúncio:
// ✅ Correto: reserva a altura do anúncio
<div
style={{
minHeight: '250px', // Altura padrão de um anúncio do Google AdSense
backgroundColor: '#f0f0f0' // Cor de fundo do espaço reservado
}}
>
{/* Script do anúncio */}
<ins className="adsbygoogle" />
</div>
Barra de aviso:
Se o site tem uma barra de aviso no topo, não deixe que ela apareça de repente:
// ❌ Incorreto: aparece de repente e empurra a página para baixo
{showBanner && <NotificationBanner />}
// ✅ Correto: reserva o espaço antecipadamente
<div style={{ minHeight: '60px' }}>
{showBanner ? <NotificationBanner /> : <div style={{ height: '60px' }} />}
</div>
Skeleton screen:
Para conteúdo carregado dinamicamente, use um skeleton como espaço reservado:
function ProductList() {
const { data, isLoading } = useQuery('products', fetchProducts);
if (isLoading) {
return (
<div className="grid grid-cols-3 gap-4">
{Array.from({ length: 6 }).map((_, i) => (
<div key={i} className="skeleton" style={{ height: '300px' }} />
))}
</div>
);
}
return (
<div className="grid grid-cols-3 gap-4">
{data.map(product => <ProductCard key={product.id} {...product} />)}
</div>
);
}
Otimize o carregamento de fontes
O carregamento de fontes também pode causar CLS, normalmente percebido como texto piscando ou mudando de tamanho.
O next/font trata esse problema automaticamente, mas você também pode ajustar o comportamento:
const inter = Inter({
subsets: ['latin'],
display: 'optional', // Se a fonte não carregar a tempo, usa a fonte do sistema
adjustFontFallback: true, // Ajusta a fonte alternativa para reduzir deslocamentos
});
Opções da propriedade display:
swap: mostra primeiro a fonte alternativa e troca quando a fonte termina de carregar; pode causar CLSoptional: se a fonte não carregar rapidamente, mantém a fonte alternativa; é a opção recomendadablock: oculta o texto por alguns instantes até a fonte carregar; não é recomendadofallback: fica entreswapeoptional
Recomendo optional. Mesmo que a fonte personalizada nem sempre apareça, a experiência do usuário será melhor.
Evite layouts responsivos controlados por JavaScript
Esta é uma das armadilhas de CLS mais comuns em 2025. Vejo muitos projetos Next.js cometendo esse erro.
Código incorreto típico:
// ❌ Incorreto: causa um CLS considerável
import { useMediaQuery } from '@/hooks/useMediaQuery';
export default function ResponsiveLayout() {
const isMobile = useMediaQuery('(max-width: 768px)');
return (
<div>
{isMobile ? (
<MobileNav />
) : (
<DesktopNav />
)}
</div>
);
}
Por que isso causa CLS?
- Na primeira renderização, o JavaScript ainda não foi executado e
isMobileéundefinedou usa um valor padrão - Depois que o JavaScript roda,
useMediaQueryretorna o valor real - O componente renderiza novamente e o layout muda bruscamente
O problema fica ainda pior com SSR, porque o servidor não conhece a largura da tela do cliente.
A forma correta: use media queries em CSS
// ✅ Correto: o CSS controla a visibilidade sem causar CLS
export default function ResponsiveLayout() {
return (
<>
<nav className="mobile-nav md:hidden">
<MobileNav />
</nav>
<nav className="desktop-nav hidden md:block">
<DesktopNav />
</nav>
</>
);
}
Ou use CSS-in-JS:
export default function ResponsiveLayout() {
return (
<div className="responsive-container">
<MobileNav />
<DesktopNav />
<style jsx>{`
.responsive-container > :global(.mobile-nav) {
display: block;
}
.responsive-container > :global(.desktop-nav) {
display: none;
}
@media (min-width: 768px) {
.responsive-container > :global(.mobile-nav) {
display: none;
}
.responsive-container > :global(.desktop-nav) {
display: block;
}
}
`}</style>
</div>
);
}
Se você realmente precisa tomar a decisão com JavaScript, por exemplo para fazer requisições de dados diferentes conforme o dispositivo, obtenha o User-Agent no servidor:
// app/page.jsx
import { headers } from 'next/headers';
export default async function Page() {
const headersList = headers();
const userAgent = headersList.get('user-agent') || '';
const isMobile = /mobile/i.test(userAgent);
return (
<div>
{isMobile ? <MobileView /> : <DesktopView />}
</div>
);
}
Assim, o servidor e o cliente produzem o mesmo resultado, sem causar CLS.
Otimização de FCP: acelere a primeira renderização de conteúdo
Embora o FCP (First Contentful Paint) não seja mais um indicador principal, ele afeta a primeira impressão. Se o usuário vê uma tela em branco por muito tempo, pode fechar a página imediatamente.
Otimize os recursos essenciais
Um FCP lento normalmente é causado por recursos que bloqueiam a renderização. Abra o painel Coverage do Chrome DevTools e veja quais arquivos CSS e JavaScript não estão sendo usados.
Incorpore o CSS essencial:
O Next.js otimiza CSS automaticamente, mas você também pode incorporar manualmente os estilos essenciais:
// app/layout.jsx
export default function RootLayout({ children }) {
return (
<html>
<head>
<style
dangerouslySetInnerHTML={{
__html: `
/* CSS essencial para a primeira tela */
body { margin: 0; font-family: system-ui; }
.hero { height: 100vh; }
`,
}}
/>
</head>
<body>{children}</body>
</html>
);
}
Adie o CSS não essencial:
Estilos que não são necessários na primeira tela, como os de modais ou áreas recolhidas, podem ser carregados depois:
// Importe CSS dinamicamente no componente
import dynamic from 'next/dynamic';
const Modal = dynamic(() => import('./Modal'), {
loading: () => <p>Carregando...</p>,
});
Gerencie scripts de terceiros
Scripts de terceiros, como Google Analytics, anúncios e plugins de redes sociais, prejudicam muito o desempenho. Em muitos sites, eles são a causa do FCP lento.
Use next/script:
O Next.js oferece o componente Script, que permite controlar quando um script será carregado:
import Script from 'next/script';
export default function Layout({ children }) {
return (
<>
{children}
{/* Google Analytics: carrega depois que a página fica interativa */}
<Script
src="https://www.googletagmanager.com/gtag/js?id=GA_MEASUREMENT_ID"
strategy="lazyOnload"
/>
<Script id="ga-init" strategy="lazyOnload">
{`
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'GA_MEASUREMENT_ID');
`}
</Script>
{/* Facebook Pixel: carregamento adiado */}
<Script
src="https://connect.facebook.net/en_US/fbevents.js"
strategy="lazyOnload"
/>
</>
);
}
Como funciona a propriedade strategy:
beforeInteractive: carrega antes que a página fique interativa; use para scripts essenciaisafterInteractive: carrega depois que a página fica interativa; é o padrãolazyOnload: carrega durante o tempo ocioso; recomendado para análise e anúnciosworker: executa em um Web Worker; é um recurso experimental
Depois que mudei todos os scripts de terceiros não essenciais para lazyOnload, o FCP melhorou 0,8 segundo.
Divisão de código e carregamento sob demanda
O Next.js faz divisão de código automaticamente, mas em alguns casos você precisa otimizar manualmente.
Carregamento sob demanda de componentes:
Para componentes que não aparecem na primeira tela, use importação dinâmica:
import dynamic from 'next/dynamic';
// Carrega o componente de comentários sob demanda
const Comments = dynamic(() => import('./Comments'), {
loading: () => <div>Carregando comentários...</div>,
ssr: false, // Não usa renderização no servidor
});
export default function BlogPost({ post }) {
return (
<article>
<h1>{post.title}</h1>
<div>{post.content}</div>
{/* Os comentários só são carregados quando o usuário chega aqui */}
<Comments postId={post.id} />
</article>
);
}
Carregamento sob demanda de bibliotecas de terceiros:
Algumas bibliotecas, como ferramentas de gráficos e editores de texto avançados, são grandes e devem ser carregadas apenas quando necessário.
Caso real: adiar uma biblioteca de gráficos economizou 800 KB
Em um projeto anterior, todas as páginas importavam o Chart.js, embora apenas o dashboard o utilizasse. Como resultado, cada página carregava 800 KB de código desnecessário.
Antes da otimização:
// ❌ Incorreto: a importação global carrega o código em todas as páginas
import { Chart } from 'chart.js';
export default function Dashboard() {
return <canvas ref={chartRef} />;
}
Depois da otimização:
// ✅ Correto: carrega apenas quando necessário
import dynamic from 'next/dynamic';
const ChartComponent = dynamic(() => import('./ChartComponent'), {
loading: () => <div>Carregando gráfico...</div>,
ssr: false,
});
export default function Dashboard() {
return <ChartComponent />;
}
// ChartComponent.jsx
import { Chart } from 'chart.js';
export default function ChartComponent() {
return <canvas ref={chartRef} />;
}
Assim, apenas quem acessa o dashboard baixa o Chart.js; as demais páginas não são afetadas.
Carregamento sob demanda de imagens:
Com exceção da imagem de LCP, as outras imagens devem usar lazy loading:
<Image
src="/feature-image.jpg"
width={600}
height={400}
loading="lazy" // É o valor padrão e pode ser omitido
alt="Apresentação do recurso"
/>
Monitoramento e otimização contínua
Concluir uma rodada de otimização não significa que o trabalho acabou. Desempenho é um processo contínuo, que exige verificações e ajustes regulares.
Automatize os testes com Lighthouse CI
Executar o Lighthouse manualmente consome tempo e é fácil esquecer. Uma opção melhor é integrá-lo ao processo de CI/CD.
Instale o Lighthouse CI:
npm install -D @lhci/cli
Arquivo de configuração lighthouserc.js:
module.exports = {
ci: {
collect: {
url: ['http://localhost:3000/', 'http://localhost:3000/products'],
numberOfRuns: 3, // Executa três vezes e usa a média
},
assert: {
assertions: {
'categories:performance': ['error', { minScore: 0.9 }], // Performance ≥ 90
'first-contentful-paint': ['error', { maxNumericValue: 2000 }], // FCP ≤ 2 s
'largest-contentful-paint': ['error', { maxNumericValue: 2500 }], // LCP ≤ 2,5 s
'cumulative-layout-shift': ['error', { maxNumericValue: 0.1 }], // CLS < 0,1
},
},
upload: {
target: 'temporary-public-storage',
},
},
};
Configuração do GitHub Actions:
# .github/workflows/lighthouse.yml
name: Lighthouse CI
on: [push]
jobs:
lighthouse:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
- run: npm install
- run: npm run build
- run: npm run start &
- run: npx @lhci/cli autorun
Assim, cada push executa automaticamente os testes do Lighthouse. Se o desempenho regredir, o CI falha e avisa que você precisa corrigir o problema.
Real User Monitoring (RUM)
O Lighthouse testa em um ambiente de laboratório, mas a experiência dos usuários reais pode ser diferente. Por isso, você precisa coletar dados reais de Core Web Vitals.
Usando Vercel Analytics:
Se o projeto está implantado na Vercel, você pode ativar o Analytics com poucos passos:
// app/layout.jsx
import { Analytics } from '@vercel/analytics/react';
export default function RootLayout({ children }) {
return (
<html>
<body>
{children}
<Analytics />
</body>
</html>
);
}
Usando a biblioteca web-vitals:
Se você não usa a Vercel, pode recorrer à biblioteca web-vitals do Google:
npm install web-vitals
// app/layout.jsx
'use client';
import { useEffect } from 'react';
import { onLCP, onFID, onCLS, onINP } from 'web-vitals';
function sendToAnalytics(metric) {
// Envia os dados ao seu serviço de análise
fetch('/api/analytics', {
method: 'POST',
body: JSON.stringify(metric),
});
}
export default function Analytics() {
useEffect(() => {
onLCP(sendToAnalytics);
onINP(sendToAnalytics);
onCLS(sendToAnalytics);
}, []);
return null;
}
Google Search Console:
O relatório de “Core Web Vitals” do Google Search Console mostra o desempenho do seu site para usuários reais e indica quais páginas precisam melhorar.
Armadilhas comuns e como resolvê-las
Para encerrar, reuni as cinco armadilhas mais frequentes.
Armadilha 1: usar JavaScript demais no cliente
Muitos desenvolvedores tentam fazer tudo com React, e a página inteira acaba dependendo de JavaScript. Se o JS falha ou demora para carregar, o usuário vê uma tela em branco.
Solução:
- Priorize Server Components, que são o padrão a partir do Next.js 13
- Use
'use client'apenas quando for necessário - Aplique Progressive Enhancement: garanta primeiro a funcionalidade básica e depois melhore a interação
Armadilha 2: ignorar o desempenho em dispositivos móveis
Um site rápido no desktop não é necessariamente rápido no celular. Dispositivos móveis têm CPU mais fraca e redes mais lentas, o que torna os problemas de desempenho mais evidentes.
Solução:
- Teste no modo “Mobile” do Lighthouse
- Limite a velocidade da rede em Chrome DevTools → Network → Throttling
- Limite a CPU em Chrome DevTools → Performance → CPU
Armadilha 3: não otimizar scripts de terceiros
Google Analytics, Facebook Pixel, Intercom, Hotjar… Cada script prejudica o desempenho.
Solução:
- Envolva todos com
next/scripte definastrategy="lazyOnload" - Revise periodicamente quais scripts são realmente necessários e quais podem ser removidos
- Considere substituir o rastreamento no cliente por rastreamento no servidor
Armadilha 4: escolher formatos de imagem inadequados
Ainda usa PNG e JPG? WebP e AVIF podem reduzir o tamanho dos arquivos em 30% a 50%.
Solução:
O componente Image do Next.js converte os formatos automaticamente, mas o servidor precisa oferecer suporte:
// next.config.js
module.exports = {
images: {
formats: ['image/avif', 'image/webp'], // Prioriza formatos modernos
},
};
Armadilha 5: ignorar o desempenho do servidor
Mesmo que o frontend esteja perfeitamente otimizado, um servidor lento prejudica todo o resultado.
Solução:
- Otimize consultas ao banco de dados com índices e redução de consultas N+1
- Use cache com Redis ou CDN
- Monitore o desempenho do servidor com ferramentas de APM, como New Relic ou Datadog
Resumo
Vamos recapitular os pontos principais.
Para otimizar o LCP:
- Defina
priorityefetchPriority="high"na imagem de LCP - Use
next/fontpara otimizar o carregamento de fontes - Priorize SSG e ISR e reduza cálculos no servidor
- Use uma CDN para diminuir o TTFB
Para otimizar o CLS:
- Defina as dimensões de todas as imagens e elementos de mídia
- Reserve espaço para conteúdo dinâmico, como anúncios e barras de aviso
- Evite hooks JavaScript para criar layouts responsivos
- Use
next/fontpara reduzir mudanças durante o carregamento de fontes
Para otimizar o FCP:
- Adie recursos não essenciais
- Envolva scripts de terceiros com
next/scripte uselazyOnload - Carregue bibliotecas grandes e componentes fora da primeira tela sob demanda
Para manter os resultados:
- Automatize os testes com Lighthouse CI
- Colete dados de usuários reais com RUM
- Revise e remova regularmente o código desnecessário
Otimização de desempenho é um ciclo de “medir → otimizar → medir novamente”. Não espere resolver tudo de uma vez; acompanhe e melhore continuamente.
Quando vi a pontuação do Lighthouse finalmente ultrapassar 90, a sensação de realização foi enorme. Mais importante, o aumento na taxa de conversão provou que o esforço valeu a pena.
Checklist de ações
Agora é a sua vez. Recomendo começar nesta ordem:
-
Faça agora:
- Teste seu projeto Next.js com o Lighthouse
- Identifique o elemento de LCP e verifique se ele usa
priority - Procure por códigos como
useMediaQueryque podem causar CLS
-
Conclua nesta semana:
- Otimize a imagem de LCP com
priorityefetchPriority - Migre as fontes para
next/font - Adicione espaços reservados para conteúdo dinâmico
- Otimize a imagem de LCP com
-
Conclua na próxima semana:
- Migre scripts de terceiros para
lazyOnload - Carregue bibliotecas grandes e componentes fora da primeira tela sob demanda
- Configure testes automatizados com Lighthouse CI
- Migre scripts de terceiros para
-
Faça continuamente:
- Verifique a pontuação do Lighthouse todos os meses
- Acompanhe o relatório de Core Web Vitals no Google Search Console
- Corrija regressões de desempenho rapidamente
Lembre-se: otimização de desempenho não é uma tarefa pontual, mas um processo contínuo. Boa otimização!
Processo completo de otimização do Core Web Vitals no Next.js
Otimize LCP, INP e CLS e aumente a pontuação do Lighthouse para mais de 90.
⏱️ Estimated time: 8 hr
- 1
Step 1: Otimizar o LCP (Largest Contentful Paint)
Meta: ≤ 2,5 s
Como otimizar:
• Use next/image para otimizar imagens, com conversão automática para WebP/AVIF
• Adicione priority às imagens essenciais da primeira tela
• Pré-carregue recursos essenciais com <link rel="preload">
• Melhore o tempo de resposta do servidor com CDN e Edge Functions
• Reduza recursos que bloqueiam a renderização, adiando CSS/JS não essencial
• Use React Server Components para reduzir o JavaScript no cliente
Ferramentas de verificação:
• Relatório Performance do Lighthouse
• Painel Performance do Chrome DevTools
• Teste on-line do WebPageTest - 2
Step 2: Otimizar o INP (Interaction to Next Paint)
Meta: ≤ 200 ms
Como otimizar:
• Reduza o tempo de execução do JavaScript com divisão de código e carregamento sob demanda
• Use React Server Components para reduzir o JavaScript no cliente
• Otimize eventos com debounce, throttle e delegação de eventos
• Evite tarefas longas que bloqueiam a thread principal usando Web Workers
• Otimize scripts de terceiros com carregamento adiado e async/defer
• Use Suspense e renderização por streaming
Ferramentas de verificação:
• Painel Performance do Chrome DevTools
• Extensão Web Vitals para Chrome
• Ferramentas de Real User Monitoring (RUM) - 3
Step 3: Otimizar o CLS (Cumulative Layout Shift)
Meta: ≤ 0,1
Como otimizar:
• Defina largura e altura de imagens e vídeos ou use aspect-ratio
• Evite inserir conteúdo dinamicamente e reserve espaço antecipadamente
• Use font-display: swap para evitar deslocamentos durante o carregamento de fontes
• Reserve espaço para anúncios para que eles não empurrem o conteúdo ao carregar
• Use CSS Grid/Flexbox em vez de posicionamento absoluto
• Evite inserir conteúdo novo acima do conteúdo existente
Ferramentas de verificação:
• Relatório de CLS do Lighthouse
• Registros de Layout Shift no painel Performance do Chrome DevTools
• Visualização de CLS no WebPageTest - 4
Step 4: Usar os recursos de desempenho do Next.js
Técnicas de otimização no Next.js:
• Use o componente Image para otimizar imagens automaticamente
• Use Font Optimization para otimizar fontes automaticamente
• Use React Server Components para reduzir o JavaScript no cliente
• Use Streaming SSR para acelerar a primeira tela
• Use Dynamic Imports para dividir o código
• Use next/dynamic para adiar o carregamento de componentes
Verificação de configuração:
• Configurações de desempenho em next.config.js
• Verifique o tamanho do bundle com npm run build - 5
Step 5: Monitorar e otimizar continuamente
Ferramentas de monitoramento:
• Relatório de Core Web Vitals do Google Search Console
• Vercel Analytics, se você usa a Vercel
• Extensão Web Vitals para Chrome
• Ferramentas de Real User Monitoring (RUM)
Otimização contínua:
• Verifique a pontuação do Lighthouse todos os meses
• Acompanhe o relatório de Core Web Vitals no Search Console
• Corrija regressões de desempenho rapidamente
• Faça testes A/B para avaliar as otimizações
FAQ
Quais são os três indicadores do Core Web Vitals?
Como otimizar o LCP?
Qual é a diferença entre INP e FID?
Como evitar CLS, ou deslocamentos de layout?
Quanto tempo leva para perceber os resultados da otimização?
Quais recursos do Next.js ajudam a melhorar o desempenho?
Como monitorar o Core Web Vitals?
19 min de leitura · Publicado em: 19 dez 2025 · Atualizado em: 4 set 2026
Guia completo Next.js
Se você chegou pela busca, o caminho mais rápido é ir para o post anterior ou próximo desta série.
Anterior
Otimização de imagens no Next.js: guia completo do componente Image
Aprenda a usar o componente Image do Next.js para corrigir carregamento lento, erros de configuração de imagens remotas e mudanças de layout. O guia inclui recursos das versões 14 e 15, exemplos práticos e técnicas de otimização de desempenho capazes de reduzir o tamanho das imagens em 60% a 80%.
Parte 26 de 51
Próximo
Guia completo de SEO em Next.js: Metadata API + dados estruturados na prática
Guia completo para configurar SEO com a Metadata API do Next.js 15, incluindo dados estruturados, Open Graph, Twitter Cards, código prático e erros comuns para aumentar o tráfego do site.
Parte 28 de 51



Comentários
Entre com GitHub para comentar