What’s new on Angular 19?

Last week, Angular 19 was released, and, as is always expected and anticipated, it brought many new features and stabilized some others. Let’s give them a quick look.

Instant rendering of style changes – HMR

What’s HMR? Hot Module Replacement, which means that instead of the browser refreshing on a style change, now the style or template modifications will be compilated, sent to the browser, and patch your app without the page refreshing, so it’s not only quicker but also allows you to retain any state you had at that moment.

https://blog.angular.dev/meet-angular-v19-7b29dfd05b84

To use it you just have to run: NG_HMR_TEMPLATES=1 ng serve.

If you want to disable it afterward you can run ng serve --no-hmr.

Standalone as default

We’ve known that Angular’s future will be standalone, and now this has been completely stated. Now all components, directives, and pipes will be created as standalone by default; you don’t need to declare them as such (standalone: true); on the contrary, now you have to declare that your component is NOT standalone (standalone: false).

When running ng update all declarations will automatically be changed.

Of course, everything with modules would still work as before, so there is nothing to worry about in terms of retro compatibility.

Hydration

Ever since Server Side Rendering became an important part of our Angular apps, the term “hydration” became an important part of our code vocabulary… and sometimes an important part of our daily headaches, but in each release, the Angular team has been making it easier and easier to use and specially, faster to run.

For those who don’t know let’s first give a quick definition of what hydration means. When using Server-Side Rendering (SSR), the webpage’s initial HTML is rendered on the server and sent to the browser, allowing it to be visually rendered (HTML and CSS) almost immediately. However, this initial render lacks interactivity because the associated JavaScript code has not yet been executed. The process of loading and executing this JavaScript to make the page interactive is called hydration. If hydration takes too long, it can lead to a poor user experience, as users may see a functional-looking page that is unresponsive or partially interactive.

Now what’s new about hydration in Angular 19?

Event Replay by default

In Angular 18 Event Replay was introduced to solve a simple, but significant, issue: What happens if the user does actions while the page is still not hydrated (so the code responsible for handling the event is unavailable)? The Event Replay functionality will create a queue of these actions and trigger them as soon as the necessary code for the event is there, so we don’t miss any of them.

Now this feature is marked as stable and is active by default, or you can enable it with:

bootstrapApplication(App, {
 providers: [
   provideClientHydration(withEventReplay())
 ]
});

Route-level render mode

When using SSR, by default Angular will server-side render all the parametrized routes in your app and prerender all routes without parameters. In Angular 19 now we have the option to configure how to handle individual routes. They can be server-side rendered, prerendered, or rendered on the client side.

export const serverRouteConfig: ServerRoute[] = [
 { path: '/login', mode: RenderMode.Server },
 { path: '/dashboard', mode: RenderMode.Client },
 { path: '/**', mode: RenderMode.Prerender },
];

For this, we have now a new type ServerRoute, instead of the usual Route. Now you can also resolve route parameters at prerender time easily:

export const routeConfig: ServerRoute = [{
path: '/product/:id',
mode: 'prerender',
async getPrerenderPaths() {
  const dataService = inject(ProductService);
  const ids = await dataService.getIds(); // ["1", "2", "3"]
  return ids.map(id => ({ id })); // `id` is used in place of `:id` in the route path.
 },
}];

Note: This feature is now in developer preview!

Signals

As with standalone components, we’ve also known for some time that the future of Angular relies on Signals, and this release brings some new complimentary APIs and the stabilization of some of the previously released.

Inputs, Outputs and View Queries

These 3 existing Signals features are now stable! You can automatically migrate all your code to use them with

ng generate @angular/core:signal-input-migration
ng generate @angular/core:signal-queries-migration
ng generate @angular/core:output-migration

Or run them all at the same time with:

ng generate @angular/core:signals

Note: This seems to work with “direct” inputs, if you use the input as a setter you’ll probably have to do some manual labor to handle them (or still use the old way)

Linked Signals

This is a new primitive which is used when there’s a need for mutable state that still tracks some higher level state.

In other words, when you have linkedSignal that DEPENDS on another baseSignal, even if you have manually changed your linkedSignal, if the baseSignal changes it also changes the linkedSignal.

Let see the Angular’s blog example:

const options = signal(['apple', 'banana', 'fig']);

// Choice defaults to the first option, but can be changed.
const choice = linkedSignal(() => options()[0]);
console.log(choice()); // apple

choice.set('fig');
console.log(choice()); // fig

// When options change, choice resets to the new default value.
options.set(['peach', 'kiwi']);
console.log(choice()); // peach

So choice is “linked” to options[0], we can have any value set on choice, but when the options array changes, the choice value changes too.

Note: This is an experimental API.

Resource

The new resource()  API is the first step in integrating signals with asynchronous operations. A resource is an asynchronous dependency that participates in the signal graph.

@Component(...)
export class UserProfile {
 userId = input<number>();

 userService = inject(UserService);

 user = resource({
   request: this.userId,
   loader: async ({request: id}) => await userService.getUser(id),
 });
}

As defined by the Angular team, a resource can be thought of as having three parts:

  1. A request function -> Request to be made in terms of signals.
  2. A loader -> Performs an asynchronous operation when the request changes and returns a value.
  3. The resulting Resource instance, which contains the value (when available) and the status (loading, resolved, error, etc).

For example:

@Component(...)
export class UserProfile {
 myTriggeringElement = input<number>();

 myAsyncService= inject(UserService);

 myResource = resource({
   request: this.myTriggeringElement ,
   loader: async (param) => await myAsyncService.getData(param.request),
 });
}

Here myTriggeringElements is a signal type trigger for the resource. As soon as it changes the resource initiates the loader async function and inserts its value into the parameters of its function, so then you can call your async service.

The param object, in this example, can also get a previous object or aboutSignal object (to handle aborted events).

You can use your resource as any normal signal, which will be an object with these values: value, hasValue, error, isLoading, and status, we can already guess by the names the uses of all but for more details, you can check the documentation.

If you are using observables at the moment, Angular has added rxResource to @jangular/core/rxjs-interop which creates a resource from an Observable-based loader.

Note: This is an experimental API.

Materials!

If you use Materials 3 and Angular 19, you are going to love these changes!

Basically, now you can easily create custom themes and/or edit existing ones AND also modify specific elements of materials.

To change the whole angular styling setup you can do.

@use '@angular/material' as mat;

html {
 @include mat.theme((
   color: (
     primary: mat.$violet-palette,
     tertiary: mat.$orange-palette,
     theme-type: light
   ),
   typography: Roboto,
   density: 0
 ));
}

To override styles in a singular materials element you can do:

@include mat.sidenav-overrides(
 (
   'content-background-color': purple,
   'container-divider-color': orange,
 )
);

Simple enough.

Also, there have been some element changes that are going to make many people (including me) very happy:

  • Two-dimensional drag and drop
    The name says it all. No more workarounds when you have a list of elements spread horizontally and vertically and need them to be dragged and dropped.
  • Support tab reordering
    Again, the name says it all. You can now reorder your tabs easily enough.
  • Time Picker component
    An extremely requested element is now available.

There is still more to the update, but this covers most of the impact changes in the day-to-day work for the Angular users.