Angular @defer em 2026: Lazy Loading Declarativo e Entrevistas

Domine os blocos @defer do Angular para lazy loading declarativo no nível de componente. Guia completo sobre gatilhos, prefetching, hidratação incremental, comportamento SSR e padrões de performance para aplicações reais.

Angular @defer lazy loading

O lazy loading baseado no roteador sempre foi a principal ferramenta de otimizacao de performance no Angular, mas possui uma limitacao estrutural: opera exclusivamente no nivel de rota. Para carregar um widget pesado condicionalmente dentro de uma pagina, era necessario recorrer a dynamic imports manuais, flags com *ngIf e configuracoes especificas de bundler. O bloco @defer, introduzido no Angular 17 e estabilizado ao longo das versoes ate o Angular 22, elimina essa complexidade ao oferecer lazy loading declarativo diretamente no template.

Com @defer, o desenvolvedor define no proprio HTML quais componentes devem ser carregados sob demanda e em quais condicoes. O compilador do Angular cuida de todo o restante: separacao de chunks, imports dinamicos e gerenciamento do ciclo de vida do carregamento. O resultado e uma reducao significativa do bundle inicial sem necessidade de reestruturar a aplicacao em rotas separadas.

O que o @defer faz na pratica

O @defer instrui o compilador do Angular a separar os componentes envolvidos em chunks JavaScript independentes, carregados apenas quando uma condicao de gatilho e ativada. O navegador baixa menos JavaScript na carga inicial, melhorando o Largest Contentful Paint (LCP) e o Time to First Byte (TTFB) sem configuracao manual de code-splitting.

Como o @defer funciona por baixo dos panos

Quando o compilador do Angular encontra um bloco @defer, ele extrai todos os componentes standalone, diretivas e pipes contidos nele para um chunk separado. O bundle principal e entregue ao navegador sem essas dependencias. Em tempo de execucao, o Angular avalia a condicao do gatilho e busca o chunk via uma chamada dinamica de import().

O bloco suporta quatro sub-blocos que controlam o que o usuario visualiza durante o ciclo de vida do carregamento:

app.component.htmltypescript
@defer (on viewport) {
  <analytics-dashboard />
} @placeholder {
  <div class="h-64 bg-muted animate-pulse"></div>
} @loading (minimum 200ms) {
  <loading-spinner />
} @error {
  <p>Failed to load the dashboard. Please refresh.</p>
}

O @placeholder renderiza antes do gatilho ser acionado. O @loading aparece enquanto o chunk esta sendo baixado, com um parametro opcional minimum que evita flickers visuais. O @error captura falhas de rede ou erros no carregamento do chunk. O parametro minimum no @loading impede aquele flash de spinner quando o chunk carrega rapidamente a partir do cache.

Ha uma restricao fundamental: todas as dependencias dentro de @defer precisam ser standalone. Componentes declarados em um NgModule tradicional nao podem ser deferidos e serao carregados eagerly, independentemente do wrapper @defer. Nenhum erro e lancado nesse caso, mas a otimizacao simplesmente nao se aplica.

Tipos de gatilhos: controlando quando os componentes carregam

O Angular disponibiliza sete gatilhos nativos, cada um voltado para uma estrategia de carregamento diferente. Varios gatilhos podem ser combinados com ponto e virgula, sendo avaliados como condicoes OR.

product-page.component.htmltypescript
// Loads when the browser goes idle (default)
@defer {
  <recommendation-engine />
}

// Loads when the element enters the viewport
@defer (on viewport) {
  <customer-reviews [productId]="product.id" />
}

// Loads on click or keydown
@defer (on interaction) {
  <product-configurator />
} @placeholder {
  <button>Configure this product</button>
}

// Loads on mouseenter or focusin
@defer (on hover) {
  <quick-preview />
}

// Loads after 3 seconds
@defer (on timer(3s)) {
  <chat-widget />
}

// Loads immediately after initial render
@defer (on immediate) {
  <notification-center />
}

O gatilho on viewport utiliza a Intersection Observer API internamente, detectando quando o placeholder entra na area visivel do navegador. O on idle delega ao requestIdleCallback, tornando-o a escolha mais segura para conteudo abaixo da dobra. O on timer aceita duracoes em milissegundos (500ms) ou segundos (3s).

O gatilho on interaction responde a eventos de clique ou keydown, tornando-o ideal para paineis que devem carregar apenas quando o usuario manifesta intencao de uso. O on hover responde a mouseenter e focusin, util para pre-visualizacoes rapidas em interfaces desktop. O on immediate dispara logo apos o render inicial, sem aguardar o estado idle do navegador, servindo para componentes que precisam carregar cedo mas nao devem bloquear a primeira pintura.

O gatilho when para condicoes reativas

Alem dos gatilhos baseados em eventos, o when aceita qualquer expressao booleana, incluindo leituras de signals:

dashboard.component.tstypescript
export class DashboardComponent {
  showAdvanced = signal(false);
}

// dashboard.component.html
@defer (when showAdvanced()) {
  <advanced-analytics />
} @placeholder {
  <button (click)="showAdvanced.set(true)">Show advanced analytics</button>
}

Combinar on e when na mesma declaracao e valido. O Angular trata ambos como OR: o bloco carrega quando qualquer uma das condicoes for satisfeita. Esse padrao e particularmente poderoso em cenarios onde funcionalidades premium devem carregar apenas para usuarios autorizados, mantendo o chunk fora do bundle de usuarios do plano gratuito. Para aprofundamento no sistema reativo do Angular, o modulo de Angular signals aborda os conceitos fundamentais.

Prefetching: separando o download da exibicao

O prefetching desacopla o momento em que o chunk e baixado do momento em que o componente e renderizado. Essa separacao elimina a latencia percebida pelo usuario, buscando o JavaScript antes que a acao de exibicao seja disparada.

settings.component.htmltypescript
@defer (on interaction; prefetch on idle) {
  <account-settings />
} @placeholder {
  <button>Open settings</button>
}

Nesse padrao, o navegador baixa o chunk de account-settings durante o tempo ocioso. Quando o usuario clica, o componente renderiza instantaneamente porque o codigo ja esta disponivel. O prefetching aceita os mesmos tipos de gatilho da clausula principal on.

Um padrao recorrente em dashboards: prefetch de componentes pesados no idle enquanto o render fica condicionado a entrada no viewport.

analytics.component.htmltypescript
@defer (on viewport; prefetch on idle) {
  <chart-widget [data]="salesData()" />
} @placeholder (minimum 100ms) {
  <div class="h-48 bg-muted rounded-none"></div>
}

O minimum no @placeholder previne um flash de layout quando o usuario faz scroll rapido e o componente deferido carrega quase instantaneamente.

Pronto para mandar bem nas entrevistas de Angular?

Pratique com nossos simuladores interativos, flashcards e testes tecnicos.

@defer e renderizacao no lado do servidor (SSR)

No servidor, os blocos @defer renderizam o conteudo do @placeholder por padrao. O componente deferido nunca e renderizado no lado do servidor. Esse comportamento e intencional: o download de chunks so faz sentido no contexto do navegador.

Para conteudo critico de SEO, isso significa que o @placeholder precisa conter HTML semanticamente relevante, e nao apenas um spinner:

blog-post.component.htmltypescript
@defer (on viewport) {
  <comment-section [postId]="post.id" />
} @placeholder {
  <section aria-label="Comments">
    <h2>Comments</h2>
    <p>Loading comments...</p>
  </section>
}

Motores de busca indexam o conteudo do placeholder durante o SSR. Um placeholder descritivo garante que a pagina permaneca semanticamente completa para crawlers e leitores de tela. Em aplicacoes que dependem de trafego organico, a presenca de marcacao estruturada no placeholder impacta diretamente o posicionamento nos resultados de busca.

Hidratacao incremental com @defer

O Angular 19 introduziu a hidratacao incremental em developer preview, e o Angular 22 a estabiliza como funcionalidade pronta para producao. A hidratacao incremental estende o @defer com um gatilho hydrate que controla quando um componente renderizado no servidor se torna interativo no cliente.

product-page.component.htmltypescript
@defer (hydrate on viewport) {
  <product-gallery [images]="product.images" />
}

@defer (hydrate on interaction) {
  <product-reviews [productId]="product.id" />
}

Diferentemente do @defer padrao, a hidratacao incremental renderiza o HTML completo do componente no servidor. O cliente recebe a marcacao completa mas posterga a hidratacao do componente ate que o gatilho seja acionado. O resultado: o navegador pinta a pagina inteira imediatamente, enquanto a execucao do JavaScript e adiada.

No caso da galeria de produtos, o HTML completo e enviado pelo servidor, mas o JavaScript do componente so e hidratado quando o elemento entra no viewport. As avaliacoes do produto, por sua vez, sao hidratadas apenas quando o usuario interage com a secao. Essa abordagem reduz drasticamente o Total Blocking Time (TBT) em paginas com multiplos componentes pesados.

A equipe do Angular reporta melhorias consistentes de 40 a 50% nos scores de LCP ao utilizar hidratacao incremental em paginas com conteudo pesado. O Angular DevTools na versao 22 permite visualizar os estados de hidratacao (pendente, hidratado, erro) diretamente no inspetor de componentes, facilitando o debugging em aplicacoes complexas.

Padroes do mundo real e estrategia de performance

Padrao 1: Dashboard pesado com multiplos paineis deferidos

Dashboards corporativos frequentemente carregam dezenas de widgets simultaneamente, impactando o tempo de carregamento inicial. O @defer permite que cada painel seja carregado independentemente, priorizando o conteudo visivel.

dashboard.component.htmltypescript
<div class="grid grid-cols-2 gap-4">
  @defer (on viewport; prefetch on idle) {
    <sales-chart />
  } @placeholder {
    <skeleton-card height="300px" />
  }

  @defer (on viewport; prefetch on idle) {
    <user-activity-feed />
  } @placeholder {
    <skeleton-card height="300px" />
  }

  @defer (on interaction) {
    <export-panel />
  } @placeholder {
    <button>Export data</button>
  }
</div>

Os graficos de vendas e o feed de atividades sao prefetchados em idle e renderizados ao entrar no viewport. O painel de exportacao permanece descarregado ate que o usuario o solicite explicitamente, economizando o download de um chunk inteiro para usuarios que nunca exportam dados. Skeleton cards no placeholder mantem a estabilidade visual do layout, evitando Cumulative Layout Shift (CLS).

Padrao 2: Carregamento condicional de funcionalidades com signals

Esse padrao e comum em aplicacoes SaaS que oferecem funcionalidades diferenciadas por plano de assinatura. O componente premium so e baixado se o usuario realmente tem acesso a ele.

editor.component.tstypescript
export class EditorComponent {
  user = inject(UserService).currentUser;
  isPremium = computed(() => this.user()?.plan === 'premium');
}

// editor.component.html
@defer (when isPremium()) {
  <ai-assistant />
} @placeholder {
  <upgrade-banner />
}

Usuarios do plano gratuito nunca baixam o chunk do assistente de IA. O banner de upgrade serve simultaneamente como placeholder e como prompt de conversao. Esse padrao demonstra como @defer pode ser utilizado nao apenas para otimizacao de performance, mas tambem para segmentacao de funcionalidades por perfil de usuario.

Armadilhas comuns a evitar

Aninhar blocos @defer com o mesmo gatilho causa downloads simultaneos de chunks, anulando o beneficio do carregamento gradual. A solucao e escalonar os gatilhos entre blocos aninhados:

typescript
// Avoid: both fire on idle simultaneously
@defer {
  @defer {
    <nested-heavy-component />
  }
}

// Better: outer on idle, inner on viewport
@defer (on idle) {
  <wrapper-component />
}
// Inside wrapper-component template:
@defer (on viewport) {
  <nested-heavy-component />
}

Outra armadilha frequente: usar @defer para componentes que ja estao no caminho critico de renderizacao. Deferir conteudo acima da dobra (above the fold) aumenta o LCP porque o navegador precisa baixar e executar o chunk antes de pintar o componente. O @defer deve ser reservado para conteudo abaixo da dobra ou acionado pelo usuario.

Um terceiro ponto de atencao envolve componentes com dependencias compartilhadas. Se dois blocos @defer utilizam o mesmo componente auxiliar, o Angular pode duplicar essa dependencia em ambos os chunks. Monitorar o tamanho dos chunks gerados com ferramentas como source-map-explorer e fundamental para identificar duplicacoes indesejadas.

@defer vs lazy loading baseado no roteador

O lazy loading pelo roteador e o @defer sao complementares, nao concorrentes. O roteador divide a aplicacao no nivel de rota, adiando modulos de funcionalidade inteiros. O @defer divide dentro de um template, adiando componentes individuais.

AspectoLazy Loading pelo Router@defer
GranularidadeNivel de rotaNivel de componente
GatilhoEvento de navegacaoviewport, idle, interaction, hover, timer, condicao
Comportamento SSRRenderizacao completa no servidorPlaceholder no servidor
Requer standaloneNao (funciona com NgModules)Sim (apenas standalone)
PrefetchingpreloadingStrategyClausula prefetch on
Caso de usoModulos de funcionalidade, paginasWidgets, paineis, UI condicional

Uma aplicacao bem arquitetada utiliza ambas as estrategias: lazy loading pelo roteador para areas de funcionalidade de alto nivel e @defer para componentes pesados dentro dessas areas. Essa combinacao maximiza a reducao do bundle inicial enquanto mantem a experiencia de navegacao fluida.

Perguntas de entrevista sobre Angular @defer

Entrevistas tecnicas cada vez mais abordam @defer a medida que ele se torna central na estrategia de performance do Angular. A seguir estao padroes que aparecem frequentemente em perguntas de entrevista Angular.

P: O que acontece se um componente nao-standalone for colocado dentro de @defer? O componente e carregado eagerly, independentemente do bloco @defer. O compilador do Angular nao consegue extrair componentes declarados em NgModules para chunks separados. Nenhum erro e lancado, mas a otimizacao silenciosamente falha. Em auditorias de performance, essa e uma das causas mais comuns de bundles maiores do que o esperado.

P: Como o @defer se comporta durante o SSR? O servidor renderiza o conteudo do @placeholder. O componente deferido nunca e renderizado no lado do servidor. A hidratacao ocorre no cliente quando o gatilho e acionado (ou via hydrate on para hidratacao incremental). Por essa razao, e essencial que o placeholder contenha marcacao semanticamente significativa para SEO.

P: O @defer funciona com a estrategia de change detection OnPush? Sim. O @defer e uma funcionalidade no nivel de template e funciona com qualquer estrategia de change detection. O componente deferido segue sua propria configuracao de deteccao de mudancas uma vez carregado. A combinacao de @defer com OnPush e uma pratica recomendada para maximizar a performance. Para aprofundar esse tema, consulte o modulo de deteccao de mudancas.

P: Qual e a diferenca entre on idle e on immediate? on idle aguarda o navegador concluir todo o trabalho pendente via requestIdleCallback. on immediate dispara logo apos o Angular completar o passo de render inicial, sem esperar pelo estado ocioso. on immediate e util para componentes que devem carregar em breve mas nao devem bloquear a primeira pintura. Na pratica, on immediate carrega o chunk antes de on idle em paginas com muito processamento, pois nao espera a fila de tarefas esvaziar.

Para uma preparacao mais abrangente de entrevistas Angular, consulte o modulo de deteccao de mudancas e o modulo de Angular signals.

Comece a praticar!

Teste seus conhecimentos com nossos simuladores de entrevista e testes tecnicos.

Conclusao

O @defer representa uma mudanca fundamental na abordagem de otimizacao de performance no Angular, levando o lazy loading do nivel de rota para o nivel de componente com uma API puramente declarativa.

  • O @defer separa componentes standalone em chunks independentes carregados sob demanda, reduzindo o tamanho do bundle inicial sem reestruturacao de rotas
  • Sete tipos de gatilho (idle, viewport, interaction, hover, timer, immediate, when) cobrem desde estrategias passivas ate acoes explicitas do usuario
  • A clausula prefetch on desacopla o download do chunk da renderizacao do componente, eliminando a latencia percebida em componentes acionados pelo usuario
  • A hidratacao incremental (hydrate on) renderiza HTML completo no servidor enquanto posterga a execucao de JavaScript no cliente, gerando melhorias de 40-50% no LCP em paginas pesadas
  • O conteudo do @placeholder e renderizado durante o SSR e precisa ser semanticamente relevante para SEO
  • @defer complementa o lazy loading pelo roteador: rotas para divisao no nivel de funcionalidade, @defer para divisao no nivel de componente dentro dos templates
  • Componentes nao-standalone dentro de @defer sao carregados eagerly sem aviso, exigindo atencao durante auditorias de performance

Comece a praticar!

Teste seus conhecimentos com nossos simuladores de entrevista e testes tecnicos.

Desafio do dia

Você saberia encontrar o bug em Angular?

Um trecho real, um bug escondido, uma tentativa por dia. Sem conta para testar.

Anthony Fillion-Maillet

Escrito por

Anthony Fillion-Maillet

Fundador da SharpSkill

Desenvolvedor fullstack há mais de 10 anos. Dirige a SharpSkill e responde por tudo o que é publicado aqui.

Atualizado em 14 de junho de 2026

Tags

#angular
#performance
#lazy-loading

Compartilhar

Artigos relacionados