Fundamentos do Next.js 15
Otimização de Imagens com o Componente 'next/image'
Aprenda sobre otimização de imagens com o componente 'next/image'
🚀 Otimização de Imagens com o Componente next/image
Olá, futuro expert em Next.js! 👋 Nesta aula prática, vamos mergulhar em um dos componentes mais poderosos para otimização de performance em aplicações Next.js: o next/image. Imagens são frequentemente os maiores culpados por sites lentos, mas com Next.js, isso não será mais um problema para você!
1. Introdução: Por Que Otimizar Imagens? 🖼️⚡
No mundo do desenvolvimento web, a velocidade é tudo. Usuários esperam que os sites carreguem instantaneamente, e motores de busca como o Google penalizam sites lentos. Imagens, embora essenciais para a estética e a comunicação, são frequentemente os arquivos mais pesados de uma página, impactando diretamente o tempo de carregamento e métricas vitais como o LCP (Largest Contentful Paint) e o CLS (Cumulative Layout Shift).
É aqui que o componente next/image entra em cena! Ele é uma extensão do elemento HTML <img> que vem com otimizações automáticas de performance, garantindo que suas imagens sejam entregues da forma mais eficiente possível, sem que você precise ser um especialista em otimização de imagens.
Benefícios chave do next/image:
- Redimensionamento e Otimização Automática: Gera diferentes tamanhos e formatos (como WebP ou AVIF, quando suportado) para cada dispositivo.
- Lazy Loading: Carrega imagens apenas quando elas entram (ou estão prestes a entrar) na viewport do usuário, economizando largura de banda.
- Prevenção de Layout Shift (CLS): Garante que o espaço para a imagem seja reservado antes do carregamento, evitando pulos inesperados na página.
- Priorização de Imagens: Permite marcar imagens importantes para carregamento antecipado (LCP).
Vamos colocar a mão na massa!
2. Explicação Detalhada com Exemplos 🧑💻
O componente next/image é projetado para ser fácil de usar, mas poderoso. Ele substitui a tag <img> padrão do HTML e adiciona inteligência.
2.1. Uso Básico com Imagens Estáticas 🏞️
Para usar o next/image, você precisa importá-lo e fornecer o src (caminho da imagem), alt (texto alternativo para acessibilidade) e, crucialmente, as propriedades width e height.
Por que width e height são obrigatórios (para a maioria dos casos)?
Estas propriedades são fundamentais para que o Next.js possa calcular o aspect ratio da imagem e reservar o espaço correto no layout antes da imagem ser carregada. Isso previne o CLS (Cumulative Layout Shift), uma métrica importante de performance.
import Image from 'next/image';
import minhaImagemLocal from '../public/images/minha-imagem.jpg'; // Importe a imagem local
export default function MyImageComponent() {
return (
<div>
<h1>Minha Imagem Otimizada</h1>
<Image
src={minhaImagemLocal} // Caminho para a imagem local
alt="Uma descrição clara da imagem"
width={500} // Largura original da imagem em pixels
height={300} // Altura original da imagem em pixels
priority // Opcional: para imagens LCP, carregá-las mais cedo
/>
<p>Esta imagem foi otimizada automaticamente pelo Next.js!</p>
</div>
);
}Observações:
- Para imagens locais (dentro do seu projeto), o Next.js automaticamente detecta
widtheheightse você importar a imagem como no exemplo. Se você usar uma string para osrcde uma imagem local, precisará fornecerwidtheheightmanualmente. - A propriedade
prioritydeve ser usada para imagens que são consideradas "Largest Contentful Paint" (LCP) – ou seja, as imagens mais importantes que aparecem primeiro na tela. Isso as carrega mais cedo, melhorando a métrica LCP.
2.2. Otimizando Imagens Remotas 🌐
Para imagens hospedadas externamente (URLs), você precisa informar ao Next.js quais domínios são permitidos para otimização, por questões de segurança e performance. Isso é feito no arquivo next.config.js.
/** @type {import('next').NextConfig} */
const nextConfig = {
images: {
// A forma mais moderna e recomendada de configurar domínios de imagens
remotePatterns: [
{
protocol: 'https',
hostname: 'assets.example.com', // Substitua pelo seu domínio
port: '',
pathname: '/my-bucket/**', // Opcional: especifica um caminho dentro do domínio
},
{
protocol: 'https',
hostname: 'images.unsplash.com', // Exemplo para Unsplash
},
],
// A propriedade 'domains' ainda funciona, mas 'remotePatterns' é mais flexível
// domains: ['assets.example.com', 'images.unsplash.com'],
},
};
export default nextConfig;Após configurar next.config.js, você pode usar imagens remotas:
import Image from 'next/image';
export default function RemoteImageComponent() {
return (
<div>
<h1>Imagem Remota Otimizada</h1>
<Image
src="https://images.unsplash.com/photo-1600000000000-000000000000?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1000&q=80"
alt="Uma montanha com neve e pinheiros"
width={1000} // Largura original da imagem
height={667} // Altura original da imagem
quality={80} // Opcional: Qualidade da imagem (0-100, padrão é 75)
/>
<p>Esta imagem remota também foi otimizada!</p>
</div>
);
}2.3. O Prop fill: Preenchendo o Container 📏
Às vezes, você quer que uma imagem preencha completamente seu elemento pai, e não se importa com suas dimensões intrínsecas. Nesses casos, use a propriedade fill. Quando fill é usado, width e height não são necessários no Image component, mas o elemento pai deve ter position: relative (ou absolute, fixed, sticky).
import Image from 'next/image';
export default function FillImageComponent() {
return (
<div style={{ position: 'relative', width: '100%', height: '300px' }}>
<Image
src="https://images.unsplash.com/photo-1500000000000-000000000000?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1600&q=80"
alt="Uma paisagem urbana ao pôr do sol"
fill // A imagem preencherá o container pai
style={{ objectFit: 'cover' }} // Como a imagem deve se ajustar ao container
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw" // Opcional: para controle responsivo
/>
</div>
);
}Propriedades importantes com fill:
style={{ objectFit: 'cover' }}: Controla como a imagem se encaixa no container. Outras opções incluemcontain,fill,none,scale-down.sizes: Esta propriedade é crucial para imagens comfille para imagens responsivas em geral. Ela informa ao navegador qual será o tamanho da imagem em diferentes breakpoints, permitindo que o Next.js gere osrcsetmais otimizado.
2.4. Outras Propriedades Úteis ✨
unoptimized: Um booleano. Setrue, a imagem será servida como está, sem otimização. Útil para GIFs ou quando você já tem uma solução de otimização externa.placeholder: Define um placeholder para a imagem enquanto ela carrega."blur": UsablurDataURL(gerado automaticamente para imagens locais estáticas, ou você pode fornecer um manualmente para imagens remotas)."empty": Um espaço vazio é reservado.
loading: Define o comportamento de carregamento."lazy"(padrão): Carrega quando a imagem está próxima da viewport."eager": Carrega imediatamente. Use comprioritypara imagens LCP.
3. Código de Exemplo Oficial (Adaptado) 📄
Vamos consolidar alguns dos conceitos em um exemplo mais completo, inspirado na documentação oficial do Next.js.
import Image from 'next/image';
import localHeroImage from '../public/images/hero-background.jpg'; // Certifique-se de ter esta imagem em public/images/
export default function HomePage() {
return (
<main style={{ maxWidth: '1200px', margin: '0 auto', padding: '20px' }}>
<h1>Bem-vindo à Nossa Página Otimizada!</h1>
<section style={{ marginBottom: '40px' }}>
<h2>Imagem de Destaque (Local e Prioritária)</h2>
<div style={{ position: 'relative', width: '100%', height: '400px', borderRadius: '8px', overflow: 'hidden' }}>
<Image
src={localHeroImage}
alt="Pessoas trabalhando em um escritório moderno, com foco em colaboração."
fill // A imagem preencherá o div pai
style={{ objectFit: 'cover' }}
priority // Esta é uma imagem LCP, então a priorizamos
quality={75} // Qualidade padrão, mas podemos ajustar
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 80vw, 1200px"
/>
</div>
<p>Esta imagem é carregada com prioridade máxima e otimizada para o tamanho da tela.</p>
</section>
<section style={{ marginBottom: '40px' }}>
<h2>Galeria de Imagens Remotas (Lazy Loaded)</h2>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(280px, 1fr))', gap: '20px' }}>
<div style={{ borderRadius: '8px', overflow: 'hidden', border: '1px solid #eee' }}>
<Image
src="https://images.unsplash.com/photo-1542831371-29b0f74f94dd?q=80&w=1470&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D"
alt="Código em um monitor"
width={1470}
height={980}
quality={85}
placeholder="blur" // Mostra um blur enquanto carrega
blurDataURL="data:image/jpeg;base64,/9j/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAADAAQEASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAD/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhEDEQA/AJVAA//Z" // Exemplo de blurDataURL para imagem remota
/>
<p style={{ padding: '10px' }}>Foco no desenvolvimento.</p>
</div>
<div style={{ borderRadius: '8px', overflow: 'hidden', border: '1px solid #eee' }}>
<Image
src="https://images.unsplash.com/photo-1517694712202-14dd9538aa97?q=80&w=1470&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D"
alt="Mãos digitando em um teclado"
width={1470}
height={980}
quality={85}
placeholder="blur"
blurDataURL="data:image/jpeg;base64,/9j/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAADAAQEASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAD/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhEDEQgA/AJVAA//Z"
/>
<p style={{ padding: '10px' }}>Produtividade em ação.</p>
</div>
<div style={{ borderRadius: '8px', overflow: 'hidden', border: '1px solid #eee' }}>
<Image
src="https://images.unsplash.com/photo-1498050108023-c5249f4cd085?q=80&w=1470&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D"
alt="Configuração de múltiplos monitores de desenvolvimento"
width={1470}
height={980}
quality={85}
placeholder="blur"
blurDataURL="data:image/jpeg;base64,/9j/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAADAAQEASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAD/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwAAARECEQA/AJVAA//Z"
/>
<p style={{ padding: '10px' }}>Setup de desenvolvimento.</p>
</div>
</div>
<p>Estas imagens são carregadas sob demanda (lazy loading) e mostram um efeito de blur enquanto carregam.</p>
</section>
<section>
<h2>Imagem Não Otimizada (Exemplo)</h2>
<Image
src="https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png"
alt="Exemplo de imagem PNG com transparência"
width={600}
height={400}
unoptimized // Esta imagem não será processada pelo Next.js
/>
<p>Use `unoptimized` com cautela, apenas quando a otimização do Next.js não for desejada.</p>
</section>
</main>
);
}Lembre-se de adicionar as URLs das imagens remotas ao next.config.js!
/** @type {import('next').NextConfig} */
const nextConfig = {
images: {
remotePatterns: [
{
protocol: 'https',
hostname: 'images.unsplash.com',
},
{
protocol: 'https',
hostname: 'upload.wikimedia.org', // Para o exemplo de imagem não otimizada
},
],
},
};
export default nextConfig;4. Exercícios e Desafios Práticos 🛠️
Agora é a sua vez de aplicar o que aprendemos! Crie um novo projeto Next.js ou utilize um existente para os desafios abaixo.
Desafio 1: Galeria de Produtos com Imagens Locais Otimizadas
Objetivo: Criar uma pequena galeria de produtos usando imagens locais e o componente next/image.
Tarefas:
- Crie um novo projeto Next.js (
npx create-next-app@latest my-image-gallery --ts --eslint). - Adicione 3-4 imagens de produtos (pode ser qualquer imagem, salve-as em
public/images/). - Crie uma página (
app/gallery/page.tsx) que exiba essas imagens em um layout de grade (grid). - Para cada imagem, use o componente
next/image. - Certifique-se de que
widtheheightestejam corretos para cada imagem. - Adicione um
alttext descritivo para cada imagem. - Desafio extra: Use a propriedade
placeholder="blur"e observe o efeito de carregamento.
Desafio 2: Banner Hero Responsivo com fill
Objetivo: Criar um componente de banner hero que usa uma imagem remota e preenche seu container de forma responsiva.
Tarefas:
- Em seu projeto Next.js, adicione uma URL de imagem remota ao
next.config.js(ex: de Unsplash). - Crie um componente
components/HeroBanner.tsx. - Dentro deste componente, crie um
divpai composition: relativee uma altura fixa (ex:height: 400px). - Use o
next/imagecom a propriedadefillpara que a imagem preencha odivpai. - Aplique
style={{ objectFit: 'cover' }}para garantir que a imagem cubra todo o espaço sem distorção. - Adicione a propriedade
prioritypara indicar que é uma imagem LCP. - Integre este componente na sua
app/page.tsx. - Desafio extra: Experimente diferentes valores para
sizese redimensione a janela do navegador para ver como osrcsetse adapta no inspetor de elementos.
Desafio 3: Comparação de Performance (Opcional, mas Recomendado!) 📈
Objetivo: Entender o impacto real do next/image na performance.
Tarefas:
- Escolha uma das imagens do Desafio 1 ou 2.
- Crie uma nova página ou seção onde você exibe a mesma imagem, mas uma vez com
next/imagee outra com a tag<img>padrão do HTML.- Para a tag
<img>, você pode usarwidtheheightpara evitar CLS, mas não terá as otimizações de formato esrcsetautomáticas.
- Para a tag
- Abra as "Developer Tools" do seu navegador (F12).
- Vá para a aba "Network" e recarregue a página (com cache desabilitado).
- Compare:
- O tamanho do arquivo transferido para cada imagem.
- O tempo de carregamento.
- O formato da imagem (WebP/AVIF vs. original).
- Desafio extra: Use a aba "Performance" ou "Lighthouse" do Chrome para gerar um relatório e veja as diferenças nas métricas de performance (principalmente LCP e CLS).
5. Resumo e Próximos Passos 🎉
Parabéns! Você dominou o uso do componente next/image, uma ferramenta essencial para construir aplicações Next.js rápidas e eficientes. Lembre-se dos pontos chave:
next/imageotimiza automaticamente suas imagens para diferentes dispositivos e formatos.- Sempre use
widtheheight(oufill) para prevenir o CLS. - Configure
remotePatternsemnext.config.jspara imagens externas. - Use
prioritypara imagens LCP. - Explore
placeholderpara melhorar a experiência do usuário durante o carregamento.
A otimização de imagens é apenas uma parte da otimização de performance em Next.js. Nos próximos módulos, continuaremos explorando outras técnicas e componentes para garantir que suas aplicações sejam incrivelmente rápidas!
Próximos Passos:
- Continue praticando com
next/imageem seus projetos. - Explore a documentação oficial do Next.js para
next/imagepara detalhes mais avançados, como loaders personalizados. - Comece a pensar em como a otimização de assets como fontes e vídeos também pode impactar a performance.
Até a próxima aula! 🚀