dev-resources.site
for different kinds of informations.
Deferred loading with @defer: Optimize Your App's Performance
Angular continues to innovate with each release, and the introduction of @defer blocks in Angular v19 is a game-changer for optimizing web application performance. This blog post explores Angular's @defer feature, explaining its functionalities, use cases, and practical code examples to help you leverage it effectively.
What are defer Blocks?
Deferrable views, also known as @defer blocks, enable lazy loading for sections of your Angular templates. They reduce the initial bundle size by deferring the loading of code that is not essential for the initial rendering of a page. This improves initial load times and Core Web Vitals (CWV) such as Largest Contentful Paint (LCP) and Time to First Byte (TTFB).
You can declaratively wrap a section of your template in a @defer
block to defer its loading:
@defer {
<large-component />
}
This loads the dependencies of large-component
only when required, improving performance.
Key Features of defer Blocks
1. Placeholders for Better User Experience
The @placeholder
block lets you show content while the deferred section is being loaded.
@defer {
<large-component />
} @placeholder {
<p>Loading...</p>
}
Use Case: Use placeholders to provide immediate feedback to users while content loads.
2. Loading Indicators
The @loading
block is shown after the placeholder and during the loading process. It replaces the placeholder content.
@defer {
<large-component />
} @loading {
<img alt="Loading..." src="loading.gif" />
} @placeholder {
<p>Loading...</p>
}
Parameters:
-
minimum
: Minimum time to show the loading block. -
after
: Time after loading starts to display the loading block.
Example:
@defer {
<large-component />
} @loading (minimum 1s; after 100ms) {
<img alt="Loading..." src="loading.gif" />
}
3. Error Handling
The @error
block displays content when deferred loading fails.
@defer {
<large-component />
} @error {
<p>Failed to load content. Please try again.</p>
}
Triggers for Controlling Deferred Content
1. Idle Trigger
Defers loading until the browser is idle:
@defer (on idle) {
<large-component />
} @placeholder {
<p>Loading...</p>
}
2. Viewport Trigger
Loads content when it enters the viewport:
@defer (on viewport) {
<large-component />
} @placeholder {
<p>Scroll down to load...</p>
}
3. Interaction Trigger
Loads content upon user interaction, such as clicks:
@defer (on interaction) {
<large-component />
} @placeholder {
<button>Click to load</button>
}
4. Hover Trigger
Loads content when the mouse hovers over a specified area:
@defer (on hover) {
<large-component />
} @placeholder {
<p>Hover to load content...</p>
}
5. Timer Trigger
Loads content after a set duration:
@defer (on timer(2s)) {
<large-component />
} @placeholder {
<p>Loading in 2 seconds...</p>
}
6. Custom Conditional Trigger
Loads content based on a custom condition:
@defer (when isReady) {
<large-component />
} @placeholder {
<p>Waiting for readiness...</p>
}
Combining Prefetching and Deferred Loading
You can specify a prefetch trigger to preload dependencies before they are needed:
@defer (on interaction; prefetch on idle) {
<large-component />
} @placeholder {
<button>Click to load</button>
}
This starts prefetching when the browser is idle but only renders the content when the user interacts.
Testing defer Blocks
Angular provides APIs to test @defer blocks using TestBed. You can control and verify different states manually:
it('should test defer block states', async () => {
TestBed.configureTestingModule({ deferBlockBehavior: DeferBlockBehavior.Manual });
@Component({
template: `
@defer {
<large-component />
} @placeholder {
Placeholder
} @loading {
Loading...
}
`
})
class TestComponent {}
const fixture = TestBed.createComponent(TestComponent);
const deferBlock = (await fixture.getDeferBlocks())[0];
expect(fixture.nativeElement.innerHTML).toContain('Placeholder');
await deferBlock.render(DeferBlockState.Loading);
expect(fixture.nativeElement.innerHTML).toContain('Loading...');
await deferBlock.render(DeferBlockState.Complete);
expect(fixture.nativeElement.innerHTML).toContain('large-component works!');
});
Best Practices for Using defer Blocks
- Avoid Nested Defers: Use distinct triggers for nested @defer blocks to prevent cascading loads.
- Prevent Layout Shifts: Avoid deferring components visible during initial load to minimize cumulative layout shift (CLS).
Cheat Sheet for Angular Blocks
-
Basic usage:
@defer { <component /> }
- Defers the content inside the block. -
Placeholder:
@defer { <cmp /> } @placeholder { <p>Loading...</p> }
- Placeholder shown before loading. -
Loading:
@defer { <cmp /> } @loading { <p>Loading...</p> }
- Shown during loading process. -
Error Handling:
@defer { <cmp /> } @error { <p>Error...</p> }
- Handles loading failures. -
Triggers:
@defer (on idle)
- Controls when loading starts. -
Prefetch:
@defer (on interaction; prefetch on idle)
- Preloads dependencies early.
Additional Resources
Start exploring Angular's @defer feature today and transform the performance of your Angular applications!
Featured ones: