skip to content

In an Angular @defer block, what do the @placeholder, @loading and @error sub-blocks show, and are their dependencies deferred?

level: juniorimportance: must knowfreq 55%

answer

  1. before, during, after a failed fetch
  2. all three optional
  3. their imports ship eagerly
  4. error means the chunk failed

basics

~20 s

In Angular, @placeholder shows before the @defer trigger fires, @loading replaces it while the deferred chunk downloads, and @error shows if that download fails. All three are optional, and their own dependencies are loaded eagerly.

solid answer

~40 s

`@placeholder` is what the `@defer` block shows before it triggers; without one, nothing renders there. `@loading` replaces the placeholder once loading has started and stays until the dependencies resolve; without one, the placeholder simply stays until the content swaps in. `@error` renders if fetching the deferred dependencies fails, for example on a network error. All three are optional, and none of them is deferred: any component, directive or pipe they use goes in the eager bundle, so they should stay light — a skeleton, a spinner, a message. If a load fails and there is no `@error` block, Angular reports runtime error NG0750 to the application's `ErrorHandler` and leaves the current placeholder or loading content on screen.

code

ts · 22 lines
ts
import { Component, signal } from '@angular/core';
import { VideoPlayer } from './video-player';
import { Spinner } from './spinner';

@Component({
  selector: 'app-lesson',
  imports: [VideoPlayer, Spinner],
  template: `
    @defer (on viewport) {
      <app-video-player [src]="videoUrl()" />
    } @placeholder {
      <div class="player-skeleton"></div>
    } @loading {
      <app-spinner />
    } @error {
      <p>The player failed to load.</p>
    }
  `,
})
export class Lesson {
  readonly videoUrl = signal('/media/intro.mp4');
}

go deeper

for a junior

Say what each sub-block shows and when, and that all three are optional.

for a middle

Explain that sub-block dependencies are eager, what shows when a block is missing, and that @error covers loading failures only.

for a senior

Design fallbacks for chunk-load failures after deployments, and route NG0750 and render errors to your error reporting.

for a principal

Set shared placeholder and error patterns so teams keep deferred sections light and failure-tolerant across the app.

## The states of a deferrable view An Angular **`@defer` block** (v17+) moves the standalone components, directives and pipes used only inside it into lazily loaded code. Between "page rendered" and "deferred content visible", the block passes through states, and each can have its own template: | Sub-block | Shown when | Replaced by | |---|---|---| | `@placeholder` | from the first render until the block triggers | `@loading`, or the content | | `@loading` | from the moment loading starts until it finishes | the content, or `@error` | | (main `@defer` content) | once all deferred dependencies have loaded | never — the swap is final | | `@error` | if loading the dependencies fails | nothing | The states only move forward: placeholder, then loading, then complete or error. A block never goes back to its placeholder. ## `@placeholder` - Optional. Without it, **nothing** renders in the block's place until it triggers. - Required in one case: element triggers written without a target (`on viewport`, `on interaction`, `on hover`) watch the placeholder, so the block needs one with **exactly one root element**. - Its content is ordinary template content: text, elements, components. - During server-side rendering, the server outputs the placeholder for blocks without hydrate triggers. ## `@loading` - Optional. It replaces the placeholder once loading has been triggered. - If there is no `@loading` block, the **placeholder stays** on screen while the chunk downloads. - It accepts `after` and `minimum` timing parameters to avoid flicker; the placeholder accepts `minimum`. ## `@error` - Optional. It renders when fetching the deferred dependencies fails — typically a network error or a missing chunk after a new deployment. - It covers **loading** failures. An exception thrown while the deferred component itself is created or rendered is reported to the `ErrorHandler`, not turned into the `@error` state. - Without an `@error` block, a failed load keeps whatever was showing and raises **NG0750** ("`@defer` dependencies failed to load") through the application's error handling. ## Their dependencies are eager Only the main `@defer` content is split out. Anything used in `@placeholder`, `@loading` or `@error` must be available immediately, so it ships in the component's eager bundle. Two consequences: 1. Keep these blocks light: plain HTML and CSS skeletons, a small spinner component, a short message. 2. Do not reuse the deferred component itself as its own placeholder "preview". Referencing it outside the main content makes it eager, which defeats the block. ## A typical block ```html @defer (on viewport) { <app-video-player [src]="videoUrl()" /> } @placeholder { <div class="player-skeleton"></div> } @loading { <app-spinner /> } @error { <p>The player could not be loaded. <a [href]="videoUrl()">Open the video</a></p> } ``` - The skeleton reserves space and acts as the viewport target. - `app-spinner` is eager, which is fine because it is small. - The error state gives the user a working fallback instead of an empty box. ## Accessibility Screen readers that are focused on the placeholder may not announce the switch to loaded content. Wrapping the block in an element with `aria-live="polite"` makes the transitions (placeholder, loading, content or error) announced.

  • Does @error catch an exception thrown in the deferred component's constructor?
    No. `@error` renders when fetching the deferred dependencies fails. Once the code has loaded, creating and rendering the content is ordinary rendering, and an exception there is reported to the application's `ErrorHandler`; the block does not switch to its error template.
  • What does the user see if there is a @placeholder but no @loading block?
    The placeholder stays on screen while the chunk downloads, and the content replaces it when loading finishes. Angular only shows a loading state if a `@loading` block exists; the placeholder is not removed early.
  • Why should a @loading block not use a heavy charting component as its spinner?
    Dependencies of `@placeholder`, `@loading` and `@error` are loaded eagerly. A heavy component there lands in the initial bundle, so you pay for it up front to decorate a wait that deferral was meant to shorten.

saying these in an interview costs you the question

  • Components inside @placeholder are also lazy-loaded.
  • @error catches any runtime exception inside the deferred content.
  • Without @loading, the block goes blank while the chunk downloads.
  • A failed load with no @error block fails silently.
  • Once loaded, a @defer block returns to its placeholder when hidden.