What Is New in Angular 19
| Category | Highlights |
|---|---|
| New Features |
|
| Improvements |
|
| Breaking Changes |
|
| Deprecations |
|
What is linkedSignal and how does it differ from computed() in Angular 19?
linkedSignal() is a writable signal whose value is automatically recomputed whenever a source signal changes, but can also be manually overridden -- a behavior that computed() simply cannot provide. It ships as an experimental API in Angular 19.0, and it fills the gap between read-only derived state and fully independent writable state.
In practice, the canonical use case is dependent selection: imagine a list of available options stored in one signal and the currently selected item stored in another. When the option list changes, the selection must be validated against the new list. With computed() you would have a read-only result and no easy way to let the user override it; with a plain signal() you have no automatic reset on source change. linkedSignal() handles both.
protected selectedColorId = linkedSignal<Color[], number | null>({
source: this.colorOptions, // reacts to this signal
computation: (source, previous) => {
// keep previous selection only if it still exists in the new list
if (previous?.value) {
return source.some(c => c.id === previous.value) ? previous.value : null;
}
return null;
}
});
// User can still override the value manually
protected onSelect(id: number): void {
this.selectedColorId.set(id);
}
Watch out for the interaction with the resource() API: both are designed to work together. If you need the selected item's ID to drive a data fetch, wire linkedSignal directly into the request parameter of resource() for a clean, reactive pipeline with no manual subscriptions.
How does the new resource() API replace RxJS-based data loading in Angular?
The resource() API is Angular's built-in answer for async data fetching integrated with the signals model -- tracking load state, handling errors, preventing race conditions, and exposing everything as signals without writing a single RxJS subscription. It is experimental in 19.0 and expanded significantly in 19.1 and 19.2.
A basic resource wires a request signal to a loader function that returns a Promise. The resource automatically re-executes the loader whenever the request signal changes, and it cancels in-flight requests using the provided AbortSignal:
fruitId = signal('apple-id-1');
fruitDetails = resource({
request: this.fruitId,
loader: async ({ request: id, abortSignal }) => {
const res = await fetch(`/api/fruit/${id}`, { signal: abortSignal });
return res.json() as Promise<Fruit>;
}
});
// All exposed as signals
isFruitLoading = this.fruitDetails.isLoading;
fruit = this.fruitDetails.value;
fruitError = this.fruitDetails.error;
For teams that prefer to stay in RxJS, rxResource() is the Observable-based counterpart; the loader returns an Observable but all output properties remain signals. In 19.2, two further upgrades landed: a defaultValue option eliminates the initial undefined state, and a new stream option supports streaming resources via a Promise<Signal<T>> return type. Also in 19.2, the experimental httpResource() wraps Angular's HttpClient in the resource model, removing manual http.get() calls entirely:
const userResource = httpResource<User>({
method: 'GET',
url: () => `/api/users/${userId()}`, // re-fetches when userId() changes
});
Most teams will find that httpResource() replaces the typical service + subscribe pattern for read operations. Write operations (POST, PUT, DELETE) still go through HttpClient directly for now.
How does Incremental Hydration work in Angular 19, and when should you use it?
Incremental Hydration lets an Angular SSR application selectively rehydrate parts of the page on demand rather than activating the entire app at once on the client -- reducing the JavaScript work needed before the page becomes interactive. It builds directly on top of the @defer block syntax from Angular 17, which means it has no new primitives to learn.
Enable it once at the application level, then annotate deferrable sections with a hydrate trigger:
// app.config.ts
export const appConfig: ApplicationConfig = {
providers: [
provideClientHydration(
withIncrementalHydration()
)
]
};
// template
@defer (hydrate on viewport) {
<app-comments />
}
@defer (hydrate on interaction) {
<app-chat-widget />
}
@defer (hydrate never) {
<app-footer /> <!-- stays dehydrated indefinitely -->
}
Supported triggers are: idle, interaction, immediate, timer(ms), hover, viewport, never, and when condition. This matters most for content-heavy pages where large sections below the fold (comments, sidebars, footers) do not need to be interactive on load. Pair this with @defer (on viewport) for deferred rendering and hydrate on viewport for deferred hydration to get maximum control over both download and activation timing.
What breaking changes in Angular 19 require action when upgrading?
Angular 19 has four breaking changes, and the most impactful one is that standalone is now the default. Any component, directive, or pipe that should remain inside an NgModule must now carry an explicit standalone: false flag -- or the compiler will treat it as standalone, which breaks NgModule-based dependency resolution.
Running ng update @angular/core@19 applies an automated migration that handles the flag insertion for existing declarations. The remaining breaking changes are:
this.fooin templates -- template context variables are no longer accessible viathis.. If your templates containthis.someVarintending to reference a template variable, remove thethis.prefix.- TypeScript minimum version -- TypeScript 5.4 and older are dropped. Upgrade to at least 5.5 before migrating. TypeScript 5.6 support arrives with 19.0, 5.7 with 19.1, and 5.8 with 19.2.
effect()timing -- effects that were previously scheduled as microtasks now run synchronously during the change detection pass. Tests that relied on specific microtask ordering (e.g.,fixture.detectChanges()followed by a microtask flush to observe effect output) will need adjustment. TheallowSignalWritesoption is also gone; signal writes inside effects are now unconditionally allowed.
// Before Angular 19 -- flag was required to write signals inside effect()
effect(() => {
this.count.set(this.source() * 2);
}, { allowSignalWrites: true });
// Angular 19+ -- flag is gone, writes are allowed by default
effect(() => {
this.count.set(this.source() * 2);
});
What developer experience improvements shipped across the Angular 19.x branch?
Beyond the headline reactive features, the 19.x branch delivered a steady stream of DX improvements that reduce boilerplate and speed up feedback cycles across the full development workflow.
Schematic migrations
ng g @angular/core:inject-- migrates constructor injection toinject()calls across the codebase.ng g @angular/core:route-lazy-loading-- converts direct component references in route configs to dynamicloadComponent()imports.ng g @angular/core:cleanup-unused-imports-- automatically removes unused entries from standalone componentimportsarrays (v19.1).
Hot Module Replacement
CSS HMR landed in 19.0 and HTML template HMR arrived in 19.1. Style and template edits now refresh only the affected component in the browser without losing application state -- no full page reload required. This is enabled by default on the dev server.
Compiler diagnostics
Two new extended diagnostics are active in 19.0: uninvoked functions catches cases like (click)="handleClick" where the missing () means the function is never actually called; unused standalone imports flags entries in the imports array that are never referenced in the template. Both surface as compile-time warnings in the IDE via Angular Language Service and as build-time errors when the strictTemplates flag is set.
fakeAsync cleanup
In 19.0, fakeAsync() zones now automatically call flush() at the end of a test, eliminating the manual cleanup step and the cryptic "X timer(s) still in queue" error that previously bit developers who forgot to drain periodic timers.
Template literals in templates (v19.2)
Angular 19.2 adds support for untagged template literals inside Angular HTML templates. String concatenation patterns like {{ 'Hello, ' + name + '!' }} can now be written as {{ `Hello, ${name}!` }} for significantly better readability.
Initializer helpers
The verbose { provide: APP_INITIALIZER, useFactory: ..., multi: true } pattern is replaced by provideAppInitializer(() => { ... }). Equivalent helpers exist for environment and platform initializers. A migration schematic handles the conversion automatically.
Frequently Asked Questions about Angular 19
Is standalone: true the default in Angular 19, and how does ng update handle existing NgModule-based projects?
Yes, standalone is now the default for all components, directives, and pipes in Angular 19. Running ng update @angular/core@19 applies an automatic migration that inserts standalone: false on every declaration that is currently registered in an NgModule, so existing projects continue to compile without manual changes.
What replaced the APP_INITIALIZER token in Angular 19?
Angular 19 introduced provideAppInitializer(), provideEnvironmentInitializer(), and providePlatformInitializer() as cleaner alternatives. They accept a factory function directly without the verbose multi-provider boilerplate. A schematic at ng g @angular/core:app-initializer handles migration automatically.
Does Angular 19 still support RxJS-based data loading or is resource() a replacement?
RxJS-based loading is fully supported. The resource() and rxResource() APIs are additive alternatives -- rxResource() in particular accepts an Observable loader and exposes signal outputs, so teams can adopt signals incrementally while keeping existing HttpClient and RxJS pipelines untouched.
How does the new effect() timing change affect unit tests in Angular 19?
Effects now execute during the change detection pass rather than as microtasks, which means tests that previously relied on Promise.resolve() or flushMicrotasks() to observe effect output may need an additional fixture.detectChanges() call. Tests using fakeAsync benefit from the new automatic flush() at test teardown, which removes the need to manually call discardPeriodicTasks().
What TypeScript versions are supported across the Angular 19 branch?
Angular 19.0 supports TypeScript 5.5 and 5.6; Angular 19.1 adds TypeScript 5.7 support; Angular 19.2 adds TypeScript 5.8 support. TypeScript versions below 5.5 are no longer supported anywhere in the 19.x branch.
Can I enable Incremental Hydration alongside existing defer blocks in an Angular 19 SSR app?
Yes. Incremental Hydration is built on top of the existing defer block infrastructure. You add withIncrementalHydration() to provideClientHydration() in your app config, then add a hydrate trigger to any existing defer block -- for example @defer (hydrate on viewport) -- without changing any other part of the block. Components without a hydrate trigger continue to hydrate using the default full-app hydration strategy.