Como fazer lazy loading de imagens no React sem prejudicar o LCP
Para fazer lazy loading de uma imagem no React, renderize um elemento img normal com
loading="lazy". Mantenha a URL real em src, adicione srcSet e sizes quando houver variantes responsivas
disponíveis e informe width e height para que o navegador possa reservar espaço.
A exceção importante é a imagem com maior probabilidade de se tornar o elemento Largest Contentful Paint (LCP) da página. Mantenha essa imagem com carregamento eager e detectável no HTML inicial. Fazer lazy loading dela atrasa justamente a requisição que a página mais precisa concluir rápido.
| Função da imagem | Carregamento | Outras marcações importantes |
|---|---|---|
| Imagem hero ou provável imagem LCP | Eager | fetchPriority="high" quando verificado |
| Outra imagem visível na primeira viewport | Eager | Dimensões e fontes responsivas |
| Imagem de conteúdo comum fora da tela | Lazy | src, srcSet, sizes e dimensões |
| Fundo CSS decorativo | Mecanismo separado | Não substitua conteúdo img significativo |
“Acima da dobra” não é um número fixo de imagens. A altura da viewport, layouts responsivos, banners e conteúdo localizado podem mover a mesma imagem para dentro ou para fora da viewport inicial. Classifique as imagens com base na página renderizada e depois verifique a escolha em tamanhos de tela representativos.
Use a prop nativa loading do React
O React repassa a prop loading ao elemento de imagem nativo do navegador. Você não precisa de um
effect, de um handler de scroll nem de uma biblioteca de Intersection Observer para uma imagem comum
fora da tela.
import type { ReactNode } from 'react'
interface ResponsiveImageProps {
alt: string
height: number
sizes: string
src: string
srcSet: string
width: number
}
export function ResponsiveImage({
alt,
height,
sizes,
src,
srcSet,
width,
}: ResponsiveImageProps): ReactNode {
return (
<img
className="responsive-image"
src={src}
srcSet={srcSet}
sizes={sizes}
width={width}
height={height}
loading="lazy"
decoding="async"
alt={alt}
/>
)
}
Mantenha uma URL de imagem real em src. Mover a única URL para um atributo data-src personalizado
faz a requisição depender do seu JavaScript e impede que o preload scanner do navegador a descubra
na marcação normal de imagens. Isso também deixa a imagem sem carregar se o script falhar.
A prop decoding="async" é uma dica separada sobre decodificação. Ela não decide quando a requisição de
rede começa e não substitui loading="lazy".
Use o componente em uma lista como você usaria qualquer outro componente React:
<ResponsiveImage
src="/images/workshop-960.webp"
srcSet="/images/workshop-480.webp 480w, /images/workshop-960.webp 960w"
sizes="(max-width: 40rem) 100vw, 40rem"
width={960}
height={640}
alt="A technician calibrating a camera rig"
/>
O lazy loading só muda o momento em que a requisição começa. Ele não redimensiona, não comprime, não converte, não armazena em cache nem descreve a imagem.
Mantenha a imagem LCP com carregamento eager
Não aplique loading="lazy" a uma provável imagem LCP. Renderize a URL dela no HTML inicial quando seu
framework React oferecer suporte a renderização no servidor, para que o navegador possa descobri-la
sem esperar a renderização no cliente ou outra requisição de dados.
import type { ReactNode } from 'react'
export function HeroImage(): ReactNode {
return (
<img
className="responsive-image"
src="/images/harbor-1280.webp"
srcSet="/images/harbor-640.webp 640w, /images/harbor-1280.webp 1280w"
sizes="100vw"
width={1280}
height={720}
loading="eager"
fetchPriority="high"
alt="Fishing boats returning to the harbor at sunrise"
/>
)
}
fetchPriority="high" é uma dica de prioridade relativa, não uma garantia sobre a ordem das requisições.
Reserve-a para a imagem LCP medida. Se várias imagens forem marcadas com prioridade alta, o navegador
terá informações menos úteis para decidir qual delas importa mais.
Uma imagem pode ser o elemento LCP no celular, mas não no desktop, ou o contrário. Use dados de desempenho de campo para confirmar qual elemento se torna o LCP para visitantes reais, em vez de presumir que toda imagem grande ou slide de carrossel é crítico.
Combine lazy loading com imagens responsivas
Uma requisição adiada ainda pode baixar um arquivo desnecessariamente grande. Com descritores de
largura como 480w, srcSet fornece os arquivos candidatos e sizes descreve a largura do espaço
renderizado. O navegador combina essas informações com a densidade de pixels do dispositivo e
escolhe um candidato.
Escreva sizes com base no layout, não no arquivo de origem. Se uma imagem ocupa toda a viewport
em um celular e uma coluna de 640 pixels em telas mais largas, informe isso:
<img
className="responsive-image"
src="/images/product-960.webp"
srcSet="/images/product-480.webp 480w, /images/product-960.webp 960w"
sizes="(max-width: 40rem) 100vw, 40rem"
width={960}
height={640}
loading="lazy"
decoding="async"
alt="Red trail shoe viewed from the side"
/>
O navegador executa a mesma seleção de candidatos responsivos para imagens eager e lazy. A decisão
de carregamento não elimina a necessidade de candidatos corretos e de um valor sizes preciso.
Reserve espaço para evitar deslocamentos de layout
Informe width e height para toda imagem de conteúdo. Os navegadores usam esses atributos para
calcular uma proporção antes de a imagem ser baixada, reservando a quantidade certa de espaço no
layout. Seu CSS ainda pode tornar a imagem fluida:
.responsive-image {
display: block;
height: auto;
max-width: 100%;
}
Os atributos precisam descrever a proporção intrínseca da imagem. Por exemplo, um img pode usar
width={960} e height={640} quando todos os candidatos têm proporção 3:2, mesmo que o navegador selecione
um arquivo de 480×320. Se fontes com direção de arte usarem recortes ou proporções diferentes, as
dimensões delas precisam descrever a fonte selecionada, e não um fallback não relacionado.
O espaço reservado para a imagem evita uma das causas de Cumulative Layout Shift (CLS), não todos os deslocamentos possíveis. Legendas, anúncios, controles de consentimento, fontes e mensagens de erro ainda podem mover a página.
Quando o Intersection Observer é apropriado
Use o comportamento de carregamento nativo do navegador para imagens comuns. Recorra ao Intersection
Observer somente quando o componente precisar de um comportamento que loading="lazy" não consegue
expressar, como iniciar uma animação, registrar um evento de visibilidade ou aplicar um fundo
decorativo custoso pouco antes de ele chegar à viewport.
Não monte condicionalmente uma imagem de conteúdo normal apenas depois que um observer disparar, a
menos que esse comportamento seja realmente necessário. A montagem condicional atrasa a descoberta,
adiciona um caminho de falha em JavaScript e exige que você projete um fallback estável. Imagens
significativas devem continuar sendo elementos img ou picture com texto alternativo útil.
React.lazy() resolve um problema diferente: ele adia o carregamento de um módulo de componente
JavaScript. Ele não adia automaticamente as requisições de imagem renderizadas por esse componente.
Gere derivados responsivos com a Transloadit
O lazy loading não conserta uma imagem de origem grande demais. Um pipeline de entrega prático gera um conjunto pequeno e limitado de larguras, otimiza cada resultado, exporta os arquivos para um armazenamento durável e registra as URLs e dimensões deles para a view React.
O Robot /image/resize da Transloadit pode criar essas variantes de largura, e o Robot /image/optimize pode otimizar os resultados compatíveis:
{
"steps": {
"small": {
"robot": "/image/resize",
"use": ":original",
"width": 480,
"resize_strategy": "fit",
"zoom": false,
"format": "webp"
},
"medium": {
"robot": "/image/resize",
"use": ":original",
"width": 960,
"resize_strategy": "fit",
"zoom": false,
"format": "webp"
},
"large": {
"robot": "/image/resize",
"use": ":original",
"width": 1440,
"resize_strategy": "fit",
"zoom": false,
"format": "webp"
},
"small_optimized": {
"robot": "/image/optimize",
"use": "small"
},
"medium_optimized": {
"robot": "/image/optimize",
"use": "medium"
},
"large_optimized": {
"robot": "/image/optimize",
"use": "large"
}
}
}
O exemplo usa zoom: false para que um upload pequeno não seja ampliado só para atingir uma largura
alvo. Depois do processamento, adicione um Robot de armazenamento ou outro Step de exportação
adequado à sua arquitetura e, em seguida, use essas URLs de entrega duráveis em src e srcSet. As
URLs de resultado da Assembly são saídas temporárias de processamento, não armazenamento permanente
da aplicação. Consulte o serviço de exportação de
arquivos para ver as integrações de armazenamento disponíveis. Não espere o
componente React criar os derivados. Se preferir transformar e armazenar em cache larguras aprovadas
sob demanda, consulte Smart CDN para transformação e entrega de imagens sob
demanda.
A aplicação React continua responsável pelas decisões de apresentação: qual imagem é significativa, o texto alternativo dela, o tamanho do espaço renderizado e se ela é crítica desde o início. A Transloadit cuida da etapa de processamento de mídia, em vez de decidir qual imagem se torna o LCP em um layout de página específico.
Meça o resultado
Verifique a página em produção em vez de confiar apenas no código-fonte do componente:
- Grave carregamentos com viewports estreitas e largas, com cache frio, nas ferramentas de desenvolvedor do navegador.
- Confirme que a requisição da imagem LCP começa cedo e não está marcada como lazy.
- Role a página e verifique se as imagens comuns fora da tela são adiadas sem aparecer tarde para o usuário.
- Inspecione o candidato
srcSetselecionado e compare as dimensões dele com o espaço renderizado. - Meça LCP e CLS com dados de campo, agrupados por modelo de página e viewport relevante.
- Bloqueie as requisições de imagem para verificar se o texto alternativo é útil e se os estados de falha são estáveis. Quando a página oferecer suporte a renderização no servidor, inspecione também o resultado renderizado no servidor antes da hidratação.
As distâncias e o agendamento do lazy loading do navegador dependem da implementação. Evite regras como “fazer lazy loading de tudo depois da terceira imagem” ou um limite fixo presumido em pixels. Meça as páginas e os dispositivos que seus usuários realmente recebem.
Erros comuns de lazy loading no React
- Fazer lazy loading da imagem hero ou da imagem LCP medida.
- Mover a única URL da imagem de
srcparadata-src. - Renderizar imagens críticas somente depois que o código do lado do cliente ou uma requisição de dados termina.
- Informar
srcSetsem um valorsizespreciso. - Omitir
widtheheightporque o CSS acaba controlando o tamanho da imagem. - Marcar todas as imagens com
fetchPriority="high". - Usar uma biblioteca de observer apenas para reproduzir o comportamento nativo do navegador.
- Presumir que
React.lazy()controla as requisições de rede de imagens. - Adiar uma imagem de origem de 3.000 pixels em vez de gerar um derivado adequado.
O padrão confiável é simples: renderize a marcação completa da imagem, mantenha as prováveis imagens LCP com carregamento eager, faça lazy loading das imagens comuns fora da tela e garanta que cada arquivo requisitado seja adequado ao espaço que o exibe.
Para conhecer as regras subjacentes do navegador, consulte o padrão HTML de lazy loading, as orientações do web.dev sobre LCP e o guia do web.dev sobre imagens responsivas.
