Angular 19 Is Here

The final release of Angular 19 has landed.

Standalone Is Now the Standard

Before you do anything else, update your project with either ng update or nx migrate. Skip the basic npm install here.

Expect the migration tooling to touch nearly every component you have. Standalone has become the default mode for all components.

The codemod strips out standalone: true wherever it appears, and for any NgModule-based component still around, it inserts standalone: false.

New Signal Helpers

linkedSignal()

Think of linkedSignal as a way to fork an existing Signal. You get a copy that you can mutate freely, and those edits won't touch the source Signal. But if that source Signal is updated anywhere, its new value flows down and overwrites your copy.

There are two obvious places where this shines:

  1. Template-driven forms, when you want a disposable draft state that mirrors a Signal but starts out as a copy.

Before Angular 19:

export class CustomerComponent {
  // original
  customer = input.required<Customer>();

  // working copy
  formCustomer: Customer | undefined = undefined;

  // synchronization
  #syncEffect = effect(() => {
    this.formCustomer = this.customer();
  })
Enter fullscreen mode Exit fullscreen mode

With Angular 19:

export class CustomerComponent {
  // original
  customer = input.required<Customer>();

  // working copy
  formCustomer = linkedSignal(this.customer)
}
Enter fullscreen mode Exit fullscreen mode
  1. Managing child state that should reset whenever a parent Signal changes. Picture a parent passing an ID down to a child component—when that ID Signal changes, the child's local state needs to be recalculated.

Before Angular 19

export class BasketComponent {
  protected readonly selectedProductId = input.required<number>();
  protected readonly amount = signal(1);

  readonly #resetEffect = effect(
    () => {
      this.selectedProductId();
      this.amount.set(0);
    },
    { allowSignalWrites: true },
  );
}
Enter fullscreen mode Exit fullscreen mode

With Angular 19

export default class BasketComponent {
  protected readonly selectedProductId = input.required<number>();

  protected readonly amount = linkedSignal({
    source: this.selectedProductId,
    computation: () => 1,
  });
}
Enter fullscreen mode Exit fullscreen mode

Keep in mind that linkedSignal currently sits in developer preview. The core behavior should hold up, but there's still room for breaking API tweaks within the v19 cycle.

resource() & rxResource()

For Signals that depend on async work—like fetching data over HTTP—the new resource function is designed to step in. A major win here is that it takes care of race conditions automatically, since an ongoing HTTP call gets aborted whenever a new request starts. All of this happens without bringing RxJS into the picture.

Before Angular 19

export class EditCustomerComponent {
  readonly #customerService = inject(CustomerService);

  readonly id = input.required({ transform: numberAttribute });
  readonly customer = signal<Customer | undefined>(undefined);

  readonly #loadEffect = effect(() => {
    this.#customerService.findById(this.id()).subscribe((customer) => {
      this.customer.set(customer);
    });
  });
}
Enter fullscreen mode Exit fullscreen mode

With Angular 19

export class EditCustomerComponent {
  readonly #customerService = inject(CustomerService);

  readonly id = input.required({ transform: numberAttribute });
  readonly customer = resource({
    request: () => this.id(),
    loader: (options) => {
      const id = options.request;
      return this.#customerService.fetchById(id, options.abortSignal);
    },
  });
}
Enter fullscreen mode Exit fullscreen mode

If you're more comfortable with RxJS, there's also rxResource, which wraps the same idea using Observables.

Both of these are still tagged as experimental—so expect breaking changes down the road.

Even so, they demonstrate a big step forward for what Angular is doing with Signals and for the whole "optional RxJS" philosophy the team keeps pushing.

Incremental Hydration

With Incremental Hydration, you can decide when different chunks of your template become interactive on the client. Hydration can be triggered by a visitor's action, a set timeout, or it can be skipped entirely.

For instance:

  • On user interaction.
  • Following a delay.
  • Or never
@Component({
  selector: 'app-edit',
  template: `
    <div class="flex">
      <div class="flex-1 p-4">
        <app-full-text #editor [(content)]="munichDescription" />
      </div>
      <div class="flex-1 prose p-4 mx-auto">
        @defer (hydrate never) {
          <!-- Component with heavy dependency to marked library -->
          <app-markdown-renderer [markdown]="munichDescription()"/>
        }
      </div>
    </div>`
})
export class EditComponent {}
Enter fullscreen mode Exit fullscreen mode

This is a result of the collaboration between Angular and Wiz, Google's internal framework that powers heavyweight apps like Gmail and Google Search.

Sure, Incremental Hydration isn't going to be critical for every project—think internal dashboards—but it makes Angular a realistic choice for an entirely new audience.

That audience consists of teams who've been picking frameworks like Next.js, Remix, or Qwik in order to build sites that load immediately and ship minimal JavaScript.

Find Out More

To dig deeper, here are the resources the team has put out:
The official release video is up on YouTube.

There's also a Q&A session recorded with Mark and Jeremy from the Angular team.

And of course, the official announcement is on the Angular blog