Angular 22.2 ·
Angular 22.2: catch rendering errors in components you create from code
Angular 22.2 adds an onError option to createComponent and createEmbeddedView. What it catches, what it misses, and how to show a fallback for a broken widget.
Angular 22.2 brought error boundaries to templates with @boundary and @error. Components you create from code get their own version: an onError option on ViewContainerRef.createComponent, on ViewContainerRef.createEmbeddedView and on the standalone createComponent() function.
What onError catches
The callback gets the error and an ErrorDetails object. It runs when the view throws during change detection or in a lifecycle hook such as ngOnInit, for example when a template binding hits bad data. Errors from views nested inside it bubble up to it too, and anything thrown that is not an Error arrives wrapped in one.
@Component({
selector: 'app-widget-slot',
template: `
<ng-container #slot />
@if (failed()) {
<p>This widget couldn't load.</p>
}
`,
})
export class WidgetSlot {
private slot = viewChild.required('slot', { read: ViewContainerRef });
failed = signal(false);
show(widget: Type<unknown>) {
this.failed.set(false);
this.slot().clear();
this.slot().createComponent(widget, {
onError: (error, details) => {
console.warn('Widget failed:', details.declarationType, error);
this.slot().clear();
this.failed.set(true);
},
});
}
}What it leaves to you
Unlike @boundary, onError does not swap anything out. Angular calls your function, treats the error as handled and leaves the failed view in the container. Removing it and showing a fallback is up to you; in a quick test, calling clear() inside the callback removed the broken widget and the rest of the page kept updating. There is no reset() either, because details.boundary is only set for @boundary. A retry means creating the component again.
Writing to a signal inside onError, as failed.set(true) does above, needs 22.2.2. In 22.2.0 and 22.2.1 the callback was not untracked yet, and the write failed with NG0600.
What it does not catch
Errors in the constructor are not caught: createComponent throws them straight away, so wrap the call in try/catch if a widget can fail that early. Errors in event listeners and in afterRender hooks still go to your ErrorHandler.
Logging is your job as well. ErrorHandler has a new optional onViewError hook, but Angular calls it for errors caught by @boundary, not for onError. And details.declarationType is the class itself. In a minified build its name won't match your source, so compare the class (details.declarationType === RevenueWidget) instead of logging names.
@boundary and ErrorDetails are in developer preview. For dashboards that load widgets from configuration, it means one widget with bad data no longer has to take the whole page down.
Sources
- Handling rendering errors (programmatic rendering guide)
- Error boundaries guide (angular.dev)
- ErrorDetails API reference
- Angular 22.2.0 release notes
I post short notes like this on LinkedIn.
Need help with this?
