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.

| Aspecto | Lazy Loading pelo Router | @defer | |--------|-------------------|--------| | Granularidade | Nivel de rota | Nivel de componente | | Gatilho | Evento de navegacao | viewport, idle, interaction, hover, timer, condicao | | Comportamento SSR | Renderizacao completa no servidor | Placeholder no servidor | | Requer standalone | Nao (funciona com NgModules) | Sim (apenas standalone) | | Prefetching | preloadingStrategy | Clausula prefetch on | | Caso de uso | Modulos de funcionalidade, paginas | Widgets, 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.

Tags

#angular
#performance
#lazy-loading

Compartilhar

Artigos relacionados