Last year, the Angular team introduced a standalone API as part of the developer preview. Now, we present a groundbreaking update for Angular version 16, bringing significant advancements in reactivity, server-side rendering, and new tools. It's all a result of dozens of improvements in response to user feedback.
In the v16 release, we introduce a completely new reactivity model for Angular, which brings significant performance and developer experience improvements.
It's fully backward compatible and interoperable with the existing system, and it enables:
- Improved real-time performance by reducing computations during change detection. After deploying Angular Signals, we expect a significant improvement in the INP Core Web Vital metric for signal-driven applications. It introduces a simpler mental model for reactivity, clearly defining dependencies between views and data flow throughout the application. It enables more precise reactivity, allowing us to monitor changes only in affected components in future versions.
- Makes Zone.js optional in future versions by using signals to notify the framework of changes in the model. It provides property computations without the penalty of re-computation in every change detection cycle.
- Enhances interoperability with RxJS by introducing a plan for reactive inputs.
- In v16, you'll find a new Signals library that is part of @angular/core, as well as an RxJS interop package - @angular/core/rxjs-interop.
The Angular Signals library allows you to define reactive values and express dependencies between them. You can learn more about the library's features in the relevant RFC document. Here's a simple example of using it in Angular:
The code above creates a computed value fullName that depends on the signals firstName and lastName. We also declare an effect whose callback function will run whenever we change the value of any of the observed signals - in this case, fullName, which indirectly depends on firstName and lastName.
When we set the value of firstName to "Mark", the browser will log:
Name changed: Mark Doe
You'll be able to easily "lift" signals to observables using functions from the @angular/core/rxjs-interop package, which is available as part of the v16 release!
Here's how you can transform a signal into an observable:
... and here's an example of transforming an observable into a signal to avoid using the async pipe:
Many Angular users want to end data flows when an action related to a particular topic concludes. The following pattern illustrates this and is quite common:
A new RxJS operator called takeUntilDestroyed has been introduced, simplifying this pattern to the following form:
By default, this operator will inject the current cleanup context. For example, it will use the component's lifecycle in a component.
takeUntilDestroyed is especially useful when you want to tie the observable's lifecycle to the specific component's lifecycle.
Server-side Rendering and Hybridization
Based on annual developer surveys, server-side rendering emerges as a crucial improvement in Angular. In recent months, the Angular team has collaborated with the Chrome Aurora team to boost performance and enhance developer experiences in the area of hybridization and server-side rendering. Now, a developer preview of full hybridization is available without disrupting the application's functionality.
Angular Hybridization - a new option. In full hybridization without Angular destruction, Angular stops recreating the application from scratch. The framework identifies existing DOM nodes, creates internal data structures, and assigns event listeners to those nodes.
The benefits include:
- No content flickering for end users.
- Better Core Web Vitals metrics in certain scenarios.
- A future-ready architecture allowing the loading of fine-grained code using elements we'll provide later this year. Currently, it's visible as progressive delaying of route hybridization.
- Easy integration with existing applications in just a few lines of code.
- Incremental adoption of hybridization with the ngSkipHydration attribute in components that manually manipulate the DOM.
In early tests, we've seen up to a 45% improvement in Largest Contentful Paint with full hybridization!
To get started, simply add a few lines of code to your main.ts file:
New Server-side Rendering Features
In the v16 release, the extension schema for ng in Angular Universal has been updated, allowing server-side rendering to be added to projects using the standalone API. Furthermore, we now support a stricter content security policy.
Enhancements for Standalone Components, Directives, and Pipes
To support developers in migrating their applications to the standalone API, migration schematics and a migration guide have been developed. When you're in your project directory, run the following command:
Zone.js configuration has been added through a new API called bootstrapApplication when using the standalone API.
We've added this option, which allows for Zone.js configuration using the new provideZoneChangeDetection API:
Several key features from Angular CLI and the language service.
Developer Preview of esbuild-based Build System
In Angular v16, the esbuild-based build system enters the developer preview phase! Previous tests have shown over a 72% improvement in production build performance.
Now, ng serve will use Vite as the development server, and esbuild powers both the development and production processes!
To enable this feature, add the following to angular.json:
Improved Unit Testing with Jest and Web Test Runner
Experimental support for Jest has been added, and in future versions, migration from Karma to Web Test Runner is possible.
Enhancing Developer Experience
In v16, you can now mark inputs as required:
Passing Router Data as Component Inputs
Now you can pass the following data as component inputs for routing:
- Path parameters
- Query parameters
- Location data
You can enable this feature using withComponentInputBinding as part of the provided router.
Angular's lifecycle hooks provide various opportunities to hook into different points in your application's execution. One request over the years has been to provide more flexibility, such as accessing OnDestroy as an observable function.
In v16, the ability to inject OnDestroy has been introduced, giving developers the flexibility they've asked for. This new feature allows injecting a DestroyRef corresponding to a component, directive, service, or pipe, and registering an onDestroy hook. DestroyRef can be injected anywhere in the injection context, even outside the component; in this case, the onDestroy hook will be triggered when the corresponding injector is destroyed.
Self-closing tags have been added, here's an example: