Angular 22.3.0-rc.0 was published on 12 November 2026, and it carries two features that change how you handle failures and data fetching: route error boundaries, so a crashing page no longer takes down your whole app shell, and HttpClient.query() for the HTTP QUERY method. This tutorial shows how to try both in the release candidate, with working code you can drop into a standalone Angular app.

Install the release candidate

Release candidates are for testing, not production. Try them on a branch:

# with the Angular CLI (pulls the latest pre-release)
ng update @angular/core @angular/cli --next

# or pin the exact RC with npm
npm install @angular/[email protected] @angular/[email protected] \
  @angular/[email protected] @angular/[email protected]

The full change list is in the 22.3.0-rc.0 release notes.

Part 1: route error boundaries

The problem

Today, if a routed component throws during creation or rendering (a null reference in a template, a failing injection, a broken resource() loader), the error goes to the global ErrorHandler and the user is often left with a blank or half-rendered page. React developers solve this with error boundaries; Angular now has a router-level equivalent.

Add an errorComponent to a route

According to the new error boundaries guide added in the router commit, a route can declare an errorComponent. If the route’s component throws, the router renders the fallback inside the same <router-outlet>, and the rest of the layout (header, sidebar, sibling outlets) stays interactive:

// app.routes.ts
import { Routes } from '@angular/router';
import { ProductDetails } from './product-details';
import { ProductErrorFallback } from './product-error-fallback';

export const routes: Routes = [
  {
    path: 'products/:id',
    component: ProductDetails,
    errorComponent: ProductErrorFallback,
  },
];

Note that the errorComponent must be eagerly loaded, even if the main component uses loadComponent. That makes sense: you do not want your fallback to fail because a chunk could not download.

Build the fallback component

The fallback is a normal component. Declare an error input to receive the caught error. With withComponentInputBinding(), route parameters bind to it too:

// product-error-fallback.ts
import { Component, input } from '@angular/core';
import { RouterLink } from '@angular/router';

@Component({
  selector: 'app-product-error-fallback',
  imports: [RouterLink],
  template: `
    <div class="error-card">
      <h3>Failed to load product #{{ id() }}</h3>
      <p>{{ error()?.message }}</p>
      <a routerLink="/products">Back to all products</a>
    </div>
  `,
})
export class ProductErrorFallback {
  readonly id = input<string>();
  readonly error = input<Error>();
}

If the error component itself throws, the outlet does not catch it again; it bubbles to the root ErrorHandler to avoid an infinite loop. Keep fallbacks simple.

Global defaults and telemetry

Instead of adding a fallback to every route, configure defaults with withErrorBoundaries:

// app.config.ts
import { ApplicationConfig, inject } from '@angular/core';
import { provideRouter, withComponentInputBinding, withErrorBoundaries } from '@angular/router';
import { routes } from './app.routes';
import { GlobalErrorFallback } from './global-error-fallback';
import { AnalyticsService } from './analytics.service';

export const appConfig: ApplicationConfig = {
  providers: [
    provideRouter(
      routes,
      withComponentInputBinding(),
      withErrorBoundaries({
        defaultErrorComponent: GlobalErrorFallback,
        onError: (error, details) => {
          inject(AnalyticsService).logException(error, details);
        },
      }),
    ),
  ],
};

onError runs for every error a router boundary catches, which is the natural place to report to Sentry or your own logging.

Redirect from a component with RedirectCommand

Components and resource loaders can now throw a RedirectCommand to navigate away, for example when an API returns 401:

import { Component, inject, resource } from '@angular/core';
import { Router, RedirectCommand } from '@angular/router';

@Component({ selector: 'app-user-profile', template: `...` })
export class UserProfile {
  private router = inject(Router);

  userData = resource({
    loader: async () => {
      const response = await fetch('/api/user');
      if (response.status === 401) {
        throw new RedirectCommand(this.router.parseUrl('/login'));
      }
      return response.json();
    },
  });
}

If no error component is configured, the router performs the navigation and deliberately swallows the error, so it is not logged as a crash.

What boundaries do not catch

Boundaries handle view-layer errors: constructors, injection, lifecycle hooks, template expressions and component resource() loaders. Errors in guards (canActivate, canMatch) or resolve functions happen before the route activates; handle those with withNavigationErrorHandler as before. The feature is marked developer preview, so the API may still change.

Part 2: HttpClient.query()

The HTTP QUERY method is a newer standard method for “safe” requests that need a body, such as complex searches that do not fit in a URL. Unlike POST, a QUERY request is defined as safe and idempotent, so caches and retries can treat it like GET. Angular 22.3 adds a matching method:

import { Injectable, inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';

export interface ProductSearch {
  text: string;
  maxPrice?: number;
  tags?: string[];
}

@Injectable({ providedIn: 'root' })
export class ProductApi {
  private http = inject(HttpClient);

  search(criteria: ProductSearch) {
    // Sends: QUERY /api/products with a JSON body
    return this.http.query<{ items: unknown[] }>('/api/products', criteria);
  }
}

The signature mirrors post(): URL, body, then options. The tests in the commit show it accepting the usual options, including observe: 'response', responseType, timeout and Fetch options like priority.

There is a nice SSR bonus: the HTTP transfer cache now includes QUERY alongside GET and HEAD by default, so a search made during server-side rendering can be reused during hydration without the includePostRequests workaround.

Before you switch, check that your server, proxies and CDN accept QUERY. Many frameworks and gateways still reject unknown methods with 405 or 501.

Other changes in the RC

  • Router: loadComponent now runs in parallel with route resources, which can speed up lazy routes.
  • Forms: several signal forms fixes, including validator directives on custom controls.
  • Core: fixes for memory leaks and closure retention, plus more tree-shaking.
  • Compiler: reports unknown property bindings on explicit ng-template elements.

Takeaway

Route error boundaries give Angular apps a clean way to fail one page instead of the whole shell, and HttpClient.query() gives complex searches a proper, cacheable method. Try both in 22.3.0-rc.0 on a branch: add a defaultErrorComponent, wire onError to your logging, and test QUERY against your backend before the stable release.