Lazy loading é a prática de adiar o carregamento de recursos que não são necessários imediatamente. Em vez de baixar todas as imagens e iframes quando a página carrega, apenas os que estão na viewport (visíveis na tela) são carregados inicialmente. Os demais carregam conforme o usuário rola a página.

Por que usar lazy loading

Uma página de e-commerce com 40 imagens de produtos pode ter 15-20MB de imagens. Sem lazy loading, o browser tenta baixar todas ao mesmo tempo, mesmo que o usuário veja apenas 6 imagens sem rolar.

Com lazy loading: apenas as 6 imagens visíveis carregam inicialmente. O restante carrega conforme o usuário navega. Resultado: tempo de carregamento inicial até 5x menor.

Lazy loading nativo (recomendado)

Desde 2019, os principais browsers suportam lazy loading nativo com o atributo loading:

<img src="produto.webp" loading="lazy" alt="Produto" width="400" height="300">

Suporte atual: Chrome, Firefox, Edge, Safari 15.4+. Cobre mais de 95% dos usuários.

Para iframes (vídeos do YouTube, Google Maps, etc.):

<iframe src="https://www.youtube.com/embed/..." loading="lazy" title="Vídeo"></iframe>

A regra mais importante: nunca use lazy na imagem LCP

O maior erro com lazy loading é aplicá-lo indiscriminadamente em todas as imagens, incluindo o hero.

A imagem LCP (maior elemento visible da página) precisa carregar o mais rápido possível. Com loading="lazy", o browser adia o carregamento dela, piorando significativamente o LCP.

Regra prática:

  • Imagens above-the-fold (visíveis sem rolar): sem loading="lazy", e na imagem principal adicione fetchpriority="high"
  • Imagens below-the-fold: use loading="lazy"
<!-- Hero - imagem principal da página -->
<img src="hero.webp" fetchpriority="high" alt="Hero" width="1200" height="600">

<!-- Galeria de produtos - abaixo do fold -->
<img src="produto1.webp" loading="lazy" alt="Produto 1" width="400" height="400">
<img src="produto2.webp" loading="lazy" alt="Produto 2" width="400" height="400">

Definir dimensões: obrigatório com lazy loading

Sem width e height, o browser não sabe o tamanho da imagem antes de baixá-la. Isso causa CLS (Cumulative Layout Shift) quando a imagem carrega - o layout pula.

<!-- CORRETO: dimensões definidas -->
<img src="produto.webp" loading="lazy" width="400" height="300" alt="Produto">

<!-- ERRADO: sem dimensões - vai causar CLS -->
<img src="produto.webp" loading="lazy" alt="Produto">

Lazy loading de iframes pesados

Iframes são frequentemente ignorados no lazy loading, mas são dos elementos mais pesados de uma página:

  • YouTube embed: sem lazy loading, carrega scripts e assets do YouTube mesmo fora da tela
  • Google Maps: carrega toda a API de mapas imediatamente
  • Widgets de redes sociais: scripts pesados de terceiros
<!-- YouTube com lazy loading -->
<iframe 
  src="https://www.youtube.com/embed/VIDEO_ID"
  loading="lazy"
  width="560" 
  height="315"
  title="Título do vídeo"
  frameborder="0"
  allowfullscreen
></iframe>

Técnica avançada para YouTube: use uma thumbnail como preview e carregue o iframe só ao clicar:

<div class="youtube-facade" data-videoid="VIDEO_ID" style="width:560px;height:315px;background:url(thumbnail.jpg)">
  <button onclick="this.parentElement.innerHTML='<iframe src=...>'">▶ Assistir</button>
</div>

Lazy loading de componentes JavaScript

Para aplicações com JavaScript, componentes não visíveis inicialmente podem ser carregados sob demanda:

Com import() dinâmico:

// Carrega o componente só quando o usuário clica em "ver mais"
document.getElementById('ver-mais').addEventListener('click', async () => {
  const { renderGaleria } = await import('./galeria.js');
  renderGaleria();
});

Com Intersection Observer (carrega quando entra na viewport):

const observer = new IntersectionObserver((entries) => {
  entries.forEach(async (entry) => {
    if (entry.isIntersecting) {
      const { inicializar } = await import('./componente-pesado.js');
      inicializar(entry.target);
      observer.unobserve(entry.target);
    }
  });
});

document.querySelectorAll('.componente-lazy').forEach(el => observer.observe(el));
Performance Web

Seu site carrega recursos desnecessários no início?

Auditamos e implementamos lazy loading correto no seu site - sem prejudicar LCP, sem CLS, com impacto real no tempo de carregamento.

Otimizar carregamento do meu site

FAQ

O lazy loading nativo é suficiente ou preciso de biblioteca JavaScript?

Para imagens e iframes, o loading="lazy" nativo é suficiente para a maioria dos casos e tem a vantagem de não adicionar nenhum JavaScript à página. Bibliotecas como lazysizes ainda fazem sentido para suporte a browsers mais antigos ou lazy loading de imagens de fundo CSS.

Como implementar lazy loading em imagens de fundo CSS?

O loading="lazy" funciona apenas em tags <img> e <iframe>. Para backgrounds CSS, use Intersection Observer para adicionar/remover uma classe que aplica o background:

const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      entry.target.classList.add('loaded');
    }
  });
});
.section-bg { background: none; }
.section-bg.loaded { background-image: url('fundo.webp'); }

Lazy loading afeta SEO e indexação do Google?

O Googlebot renderiza JavaScript e suporta lazy loading. Contudo, imagens abaixo do fold podem demorar mais para serem indexadas. Para SEO, o mais importante é garantir que as imagens principais (above-the-fold, imagem de produto em destaque) não usem lazy loading.

Qual a “distância” que o browser usa para pré-carregar imagens lazy?

O browser começa a carregar imagens lazy quando elas estão dentro de uma margem configurada internamente (geralmente 1250px a 2500px abaixo da viewport, dependendo da velocidade de conexão detectada). Isso garante que as imagens carreguem antes do usuário rolar até elas.