Angular 20.1 – What’s New?

Angular 20.1 arrives with a collection of notable enhancements. The update brings refinements to template syntax, testing utilities, HTTP request management, and image handling that are worth examining in detail.

Binary Assignment Operators in Templates

One of the most anticipated additions in Angular 20.1 is the support for binary assignment operators directly within component templates. The full range of operators now available includes:

  • +=, -=, *=, /=, %=
  • **=, <<=, >>=, >>>=
  • &=, ^=, |=
  • &&=, ||=, ??=

Previously, modifying a variable's value directly in the template—such as incrementing a counter—required calling a dedicated method defined in the component class.

<button (click)="increment()">+</button>
increment() {
  this.counter += 1;
}

With Angular 20.1, the same logic can be expressed using the new operator syntax, making templates more concise and self-documenting.

user = { name: '' };
<button (click)="user.name ??= 'Anonymous'">Set user name</button>
<button (click)="counter += 1">+</button>

The benefits are twofold:

  • Component classes contain less boilerplate code
  • Simple imperative operations become visible and readable directly in the HTML template

Enhanced NgOptimizedImage Directive

The NgOptimizedImage directive now exposes a new decoding input, granting developers finer control over how images are decoded. This addition aligns with standard HTML attributes while offering improved performance management for image loading.

<img ngSrc="..." decoding="async" />

This parameter accepts three distinct values:

  • sync – The image is decoded immediately as part of content parsing.

This option is suitable when you need the image to appear exactly when its HTML is rendered.

  • async – Decoding happens asynchronously.

This setting ensures a smoother interface, as the decoding process occurs in the background without blocking user interactions.

  • auto – The browser applies its own default logic.

When selected, the browser makes the optimal decision for decode timing.

Extended Configuration for HttpResource and HttpClient

Cache and Priority Support

Angular 20.1 expands HttpResource with new configuration options: cache and priority, which mirror the capabilities found in the Fetch API.

The cache option dictates how—and whether—the browser leverages its internal cache during an HTTP request. This level of direct control over browser caching was previously unavailable in Angular, necessitating workarounds for developers who needed to enforce specific caching behaviors.

This comes in handy when you need to guarantee fresh data from an API, bypassing any cached responses the browser might hold.

The possible values are:

  • default – The browser employs its own caching heuristics.
  • no-store – No data is read from or written to the cache for this request.
  • reload – The network is always hit; the cache is entirely ignored.
  • force-cache – The cache is used even if the stored response is considered stale.
  • only-if-cached – The request is sent only when a corresponding cached response already exists. If the cache has no match, the request is not dispatched, and the browser produces an error.
httpResource({
  getConfig: () => ({
    url: '/api/products',
    cache: 'reload'
  })
});

Meanwhile, the priority option is an experimental addition that signals the importance of a request, potentially influencing its scheduling relative to other network operations.

This proves useful in scenarios where many simultaneous fetches are in flight and you want certain data to be processed sooner than the rest.

The available values for priority are:

  • high – Used for critical data, such as content needed to render the interface quickly.
  • low – Appropriate for non-essential requests like preloading or fetching secondary data.
  • auto – The browser applies its default prioritization scheme.
httpResource({
  getConfig: () => ({
    url: '/api/metrics',
    priority: 'low'
  })
});

It is important to note that priority is not universally supported in all browsers yet. Legacy and less recent versions—including Internet Explorer, Chrome versions below 103, Firefox below 132, Safari below 17.2, and Edge below 103—lack support. Even so, this feature lays the groundwork for future optimization opportunities.

Mode and Redirect Configurations

The HttpResource configuration in Angular 20.1 now includes the mode and redirect settings. These additions enable tighter handling of API communications, particularly for interactions with external domains.

The mode property controls how the browser evaluates the request with respect to Cross-Origin Resource Sharing (CORS).

Valid options include:

  • cors – Permits resource access from other domains, prerequisite, of course, that the remote server provides the appropriate CORS headers.
  • same-origin – Restricts requests exclusively to the domain hosting the application.
  • no-cors – Sends requests cross-origin, but by design prevents any reading of the response; highly restricted but sometimes usable for low-level operations.
httpResource({
  getConfig: () => ({
    url: 'https://api.domena-zewnetrzna.com/data',
    mode: 'cors'
  })
});

The redirect parameter deals with the browser's response to HTTP redirect statuses such as 301, 302, and 307. This option can be used to deliberately intercept automatic redirects.

Accepted values are:

  • follow – The browser automatically follows any redirects (the standard browser behavior).
  • error – An error is thrown immediately if a redirect response is encountered.
  • manual – Redirects are not followed automatically, leaving the developer to handle them programmatically.
httpResource({
  getConfig: () => ({
    url: '/api/old-endpoint',
    redirect: 'error'
  })
});

Credentials Extension

The credentials option manages how cookies, authorization headers, and other session-related data are attached to an HTTP request, especially important when dealing with cross-origin calls.

This configuration is critical for several cases:

  • Managing sessions with authentication tokens like HTTP-only cookies.
  • Reaching APIs that demand explicit client identification.
  • Interfacing with backends hosted on a different domain, port, or subdomain.

The selectable states for credentials are:

  • same-origin – The default; credentials are sent only when the request targets the same domain as the Angular app.
  • include – Cookies and authorization headers are sent unconditionally, even across different domains.
  • omit – Cookies and HTTP authentication details are never included with the request.
this.http.get('/api/user', {
  fetchOptions: {
    credentials: 'include'
  }
});

Keepalive Support

A further addition to the still-experimental HttpResource API is the keepalive option, rooted in the interface provided by the modern fetch() method. This makes it possible to designate specific requests so that:

{ keepalive: true }

These flagged requests can continue to be sent right up until the page is torn down, including when a tab closes or when the user navigates elsewhere. The utility of this is constrained to lightweight POST or GET requests that carry no body and are 64KB or less, yet the impact could still be significant for features like analytics probes or server-side session cleanup.

const resource = httpResource({
  method: 'POST',
  url: '/api/session/close',
  keepalive: true,
});

Injection Context for Lazy Loading Hooks

Developers can now use dependency injection with loadChildren() and loadComponent(). This means services and other injectable dependencies are accessible before these lazy-loading functions execute, granting greater flexibility in their internal logic.

{
  path: 'home',
  loadComponent: () => {
    const config = inject(MyFeatureConfig); // wcześniej błąd!
    return import('./home.component').then(m => m.HomeComponent);
  }
}

New Bindings Option in TestBed

Unit testing setup sees simplifying changes through a novel bindings option in TestBed. This enhancement is particularly useful for passing component inputs or @HostBinding() values directly during test component creation.

In the past, this would necessitate manually assigning each input value immediately after the testing module was bootstrapped and the component was instantiated.

it('old', () => {
  const fixture = TestBed.createComponent(MyComponent);
  fixture.componentInstance.title = 'Hello';
  fixture.componentInstance.hostClass = 'highlighted';
  fixture.detectChanges();

  const el: HTMLElement = fixture.nativeElement;
  expect(el.textContent).toContain('Hello');
  expect(el.className).toBe('highlighted');
});

Under the new syntax, these initializations can be done within a single, more explicit block:

it('new', () => {
  const fixture = TestBed.createComponent(MyComponent, {
    bindings: {
      title: 'Hello',
      class: 'highlighted', 
    }
  });

  const el: HTMLElement = fixture.nativeElement;
  expect(el.textContent).toContain('Hello');
  expect(el.className).toBe('highlighted');
});

In essence, the new bindings option provides three key improvements for testing experiences:

  • It allows for immediate assignment of inputs and host bindings.
  • It removes the requirement to write repetitive fixture.componentInstance.xxx = ... assignments.
  • It shifts tests toward a more descriptive, declarative coding style.

Introducing DestroyRef.destroyed

The DestroyRef mechanism, available since Angular version 16, lets you tap into the lifecycle events of an instance, such as a component or service, without adding explicit ngOnDestroy() implementations.

With the new addition of the destroyed property, your code can determinedly check if a given instance is still active or has already been torn down.

In the following scenario, imagine a live search feature configured with an intentional delay. If the component hosting that search is closed before it can execute, a call inside performSearch() would run against disposed resources. The guard now checks the destroyed state and skips execution, preventing operation on data that no longer exists.

search.valueChanges.pipe(debounceTime(300)).subscribe(value => {
  if (!this.destroyRef.destroyed) {
    this.performSearch(value);
  }
});

Using this pattern also protects against common development pitfalls such as ExpressionChangedAfterItHasBeenCheckedError, given that it prevents JavaScript from mutating objects that have been cleaned from the component tree.

Summary

Angular 20.1 pushes the framework further toward a more ergonomic and efficient developer experience. While the adjustments here are not sweeping, they contribute significantly to both programming comfort and long-term project stability through cleaner syntax and more powerful APIs.

Also remember to upgrade Node.js to one of the required versions—at minimum 20.19, 22.12, or 24.0—as these are prerequisites for this release.