Hydration is the process of “activating” a server-rendered page on the client side by adding interactivity. When an application is server-side rendered (SSR), the server generates an HTML document and sends it to the client. On the client side, Angular needs to take over and add interactivity to the static HTML – known as “hydration.”
In traditional Angular SSR (read more about Angular SSR implementation in my colleague – Paweł Żurawski’s – article), the entire application is hydrated in a single go once the client receives the HTML. This can be inefficient, especially if the application is large or complex, because the entire page has to be bootstrapped all at once, which can lead to delays in making the page interactive.
Incremental hydration is a cutting-edge feature aimed at optimizing the performance of server-side rendered applications by improving the loading and execution of JavaScript on the client side. This feature helps reduce the time it takes for the page to become interactive by incrementally bootstrapping parts of the application instead of doing it all at once. Angular hydrates smaller parts of the application incrementally as needed. By hydrating only the necessary parts of the application as needed, incremental hydration can also help minimize the impact of unused JavaScript, further improving performance.
With incremental hydration, Angular will break up the application into smaller “hydration zones” or “hydration chunks.” When the client receives the server-rendered HTML, Angular will start hydrating the essential or visible parts of the page first. Once those are interactive, Angular will continue to hydrate the remaining areas of the page in the background.
Incremental hydration lets you mark sections of your template with the familiar @defer syntax (introduced in Angular 17), directing Angular to lazy load and hydrate them only when certain triggers occur.
Example:
@defer (hydrate on viewport) { <your-component /> }
You can read more about incremental hydration in the documentation.
Event replay is a feature in Angular that ensures the seamless execution of user interactions in server-side rendered (SSR) or partially hydrated applications.
This mechanism captures and queues user interactions (e.g., clicks, form submissions, and scrolls) that occur before the application is fully hydrated. Once the hydration process is complete and the client-side JavaScript takes control, these queued events are replayed and handled as if they had occurred after hydration.
Starting with Angular 19, event replay is stable and enabled by default for all new applications that use server-side rendering, making them more robust and interactive without requiring additional configuration.
The Angular CLI will now generate this setup by default:
bootstrapApplication(App, { providers: [ provideClientHydration(withEventReplay()) ] });
Standalone components were introduced in v14, and over 90% of developers in the last developer survey claimed they use this feature. In Angular 19, standalone components are now the default way to define and build Angular components. This change simplifies the metadata of all your standalone components, directives, and pipes.
When updating to v19, the CLI will automatically remove the standalone metadata property for all standalone abstractions, set to false for non-standalone ones.
To enforce modern APIs in your project, a compiler flag has been introduced. It will trigger an error if it detects a component, directive, or pipe that isn’t standalone. To enable this feature, configure tsconfig.json file as follows:
{ "angularCompilerOptions": { "strictStandalone": true } } [..] [ERROR] TS-992023: Only standalone components/directives are allowed when 'strictStandalone' is enabled. [plugin angular-compiler]
The linkedSignal in Angular is an experimental API that introduces a new way to manage a state that is intrinsically linked to other reactive states. This feature is particularly useful in scenarios where a piece of a state depends on another and should update accordingly when the source state changes.
linkedSignal creates a writable signal that automatically updates its value based on a computation function whenever its source signal changes. This ensures that the dependent state remains consistent with its source.
Unlike computed signals, which are read-only, linkedSignal allows for manual updates. This means you can set its value directly, providing flexibility in managing states that may need both automatic and manual adjustments.
For simpler scenarios, you can use shorthand syntax:
animals = signal(['guinea pig', 'cat', 'dog']); selectedAnimal = linkedSignal(() => animals()[0]); console.log(selectedAnimal()); // 'guinea pig' animals.set(['elephant','rabbit']); console.log(selectedAnimal()); // 'elephant'
Every time the animals array changes, the selectedAnimal will change to the first one from a new list.
In some cases, a new value has to be computed based on the previous value. This is where you might consider using more advanced syntax with source and computation:
animals = signal<string[]>(['guinea pig', 'cat', 'dog']); selectedAnimal = linkedSignal({ source: animals, computation: (newAnimals: string[], previous?: { value: string }) => { // If the previous selection exists in the new list, keep it. Otherwise, use the first animal. return previous && newAnimals.includes(previous.value) ? previous.value : newAnimals[0]; }, }); // Keep current selection while resetting animals list (for example: re-fetching from API) console.log(this.selectedAnimal()); // 'guinea pig' this.animals.set(['horse', 'guinea pig']); console.log(this.selectedAnimal()); // 'guinea pig' this.animals.set(['horse', 'fish']); console.log(this.selectedAnimal()); // 'horse' // Since linkedSignal is writable, animal can also be selected manually (for example: after selection from UI) this.selectedAnimal.set('dog'); console.log(this.selectedAnimal()); // 'dog'
You can read more about linkedSignals in the documentation.
Angular 19 introduces experimental resource and rxResource APIs to streamline asynchronous data retrieval and management by leveraging signals and observables. It’s worth pointing out that both APIs may yet undergo changes before becoming stable in future releases.
The resource function creates a reactive data source that fetches asynchronous data and provides signals to monitor its state.
Main features:
Example usage:
const RESOURCE_URL = 'https://lorem.ipsum'; const id = signal(1); const myResource = resource({ request: () => ({ id: id() }), loader: ({ request, abortSignal }) => fetch(`${RESOURCE_URL}${request.id}`, { signal: abortSignal }).then(response => response.json()), });
In this example, myResource fetches data based on the id signal. Changing id triggers a new fetch, with the previous request aborted if still in progress.
The rxResource function offers similar functionality but, while resource() uses a promise-based loader, rxResource() integrates with RxJS observables, catering to developers who prefer using observables for asynchronous operations. It is suited for more complex scenarios involving streams of data and leverages the full power of RxJS to handle retries, cancellations, combining streams, and more. Currently, rxResource considers only the first emitted value from the observable; subsequent emissions are ignored.
Example usage:
const RESOURCE_URL = 'https://lorem.ipsum'; const id = signal(1); const myRxResource = rxResource({ request: () => ({ id: id() }), loader: ({ request }) => fromFetch(`${RESOURCE_URL}${request.id}`).pipe( switchMap(response => response.json()) ), });
Here, myRxResource fetches data as an observable stream, reacting to changes in the id signal.
In Angular 19, server route configuration has been introduced to provide developers granular control over how each route in their application is rendered. This feature allows you to specify the rendering mode for individual routes, choosing between Server-Side Rendering (SSR), Static Site Generation (SSG), or Client-Side Rendering (CSR).
To configure server routes, you define an array of ServerRoute objects, typically in a file named app.routes.server.ts. Each ServerRoute specifies a path and its corresponding rendering mode using the RenderMode enum.
Example usage:
import { RenderMode, ServerRoute } from '@angular/ssr'; export const serverRoutes: ServerRoute[] = [ { path: '', // Root path renderMode: RenderMode.Client, // Rendered on the client (CSR) }, { path: 'about', renderMode: RenderMode.Prerender, // Prerendered at build time (SSG) }, { path: 'profile', renderMode: RenderMode.Server, // Rendered on the server per request (SSR) }, { path: '**', // Wildcard route for all other paths renderMode: RenderMode.Server, // Default to SSR }, ];
Angular 19 introduces three rendering modes that can be assigned to routes:
Starting with Angular 19, the Angular CLI includes a feature that reports warnings for unused imports in your application. This enhancement aims to improve code cleanliness and developer productivity by helping you identify unnecessary or obsolete imports in your project files.
Example:
@Component({ selector: 'test-component', imports: [IconComponent], templateUrl: './test-component.component.html', }) [...] [WARNING] TS-998113: All imports are unused [plugin angular-compiler] imports: [IconComponent]
Angular evolves rapidly – the 19th version brings a wealth of improvements to enhance the developer experience and general performance of Angular apps. Analyze these functionalities and start incorporating them into your own Angular projects and workflow – they’re bound to make your life easier in various circumstances.
If you’re interested in more Angular content, check some of the other publications on our blog:
Here are answers to some common questions regarding Angular applications, features, and development.
The current version is Angular 19.
Some of the standout features include incremental hydration, stable event replay, standalone changes, the new experimental linked signal primitive, experimental streaming data retrieval APIs, hybrid rendering, and warnings for unused standalone imports.
Angular 19 was released on November 19, 2024.
It’s a comprehensive components library for material design in Angular.
The Angular Language Service helps code editors with error handling and identification, getting completions and hints, and navigating inside Angular templates.
Hot module replacement (HMR) allows developers to replace application components and modules in real time (during runtime) without losing the application state.