Fundamentos do Next.js 15

0/26 aulas0%
pratica

Otimização de Imagens com o Componente 'next/image'

Aprenda sobre otimização de imagens com o componente 'next/image'

30 min
Aula 3 de 5

🚀 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 width e height se você importar a imagem como no exemplo. Se você usar uma string para o src de uma imagem local, precisará fornecer width e height manualmente.
  • A propriedade priority deve 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 incluem contain, fill, none, scale-down.
  • sizes: Esta propriedade é crucial para imagens com fill e para imagens responsivas em geral. Ela informa ao navegador qual será o tamanho da imagem em diferentes breakpoints, permitindo que o Next.js gere o srcset mais otimizado.

2.4. Outras Propriedades Úteis ✨

  • unoptimized: Um booleano. Se true, 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": Usa blurDataURL (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 com priority para 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 width e height estejam corretos para cada imagem.
  • Adicione um alt text 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 div pai com position: relative e uma altura fixa (ex: height: 400px).
  • Use o next/image com a propriedade fill para que a imagem preencha o div pai.
  • Aplique style={{ objectFit: 'cover' }} para garantir que a imagem cubra todo o espaço sem distorção.
  • Adicione a propriedade priority para indicar que é uma imagem LCP.
  • Integre este componente na sua app/page.tsx.
  • Desafio extra: Experimente diferentes valores para sizes e redimensione a janela do navegador para ver como o srcset se 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/image e outra com a tag <img> padrão do HTML.
    • Para a tag <img>, você pode usar width e height para evitar CLS, mas não terá as otimizações de formato e srcset automáticas.
  • 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/image otimiza automaticamente suas imagens para diferentes dispositivos e formatos.
  • Sempre use width e height (ou fill) para prevenir o CLS.
  • Configure remotePatterns em next.config.js para imagens externas.
  • Use priority para imagens LCP.
  • Explore placeholder para 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/image em seus projetos.
  • Explore a documentação oficial do Next.js para next/image para 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! 🚀

© 2025 Escola All Dev. Todos os direitos reservados.

Otimização de Imagens com o Componente 'next/image' - Fundamentos do Next.js 15 | escola.all.dev.br