Angular 21 is available!

Angular 21 has been released, and I’m going to divide the new features into different categories, since a lot of the new features are experimental and not recommended for production use yet.

What’s stable and production-ready:

  • Angular’s MCP Server, now with more tools — allowing LLMs to use new Angular features from day one. The most interesting feature is the onpush_zoneless_migration tool. It can analyze your code and provide a plan to migrate your application to OnPush and zoneless change detection.
  • The Angular CLI now uses Vitest as the default test runner. And there’s an experimental migration if you want to migrate your old Jasmine tests: ng g @schematics/angular:refactor-jasmine-vitest
  • Creating a brand new Angular application with the CLI no longer includes zone.js.

What’s experimental or in developer preview, not recommended for production:

  • Signal Forms, providing a new composable and reactive forms experience built on Signals. Here is my short tutorial about the basics of Signal Forms.
  • Angular Aria is launching in Developer Preview, providing headless components built with accessibility as a priority. To install: npm install @angular/aria. It is starting with 8 components:
    • Accordion
    • Combobox
    • Grid
    • Listbox
    • Menu
    • Tabs
    • Toolbar
    • Tree

You can read more with the official v21 blog post here. I will unpack all these new features over the next few weeks in separate posts (Vitest, Aria, etc.).

Aliasing content for projection

We have discussed content projection before, as well as multi-slot content projection, a technique where we can pass multiple pieces of content from one component to another.

For instance, this can be useful when we want to customize a header, body, and footer of a card or a modal component. We need several different templates to pass to that component, and multi-slot content projection is ideal for this purpose (see an example in action on Stackblitz here).

The only downside of such content projection is that it relies on selectors, which can be CSS classes, HTML tag names, or other similar elements. While that works, it’s not a great developer experience as it does not show explicitely when a piece of HTML is meant to be projected.

This is where aliases come into play. If we have the following child component code, expecting two different pieces of projected content:

With aliases, we can now project anything into those slots by using the attribute ngProjectAs in the parent component:

That way, we no longer rely on CSS selectors. We make it explicit that an element is meant to be projected in a given slot. This results in more robust code as it can’t break when CSS or HTML structures change.

Errors, arrow functions, and local variables

This week, we’re using the newsletter’s 3-2-1 format. I’m posting a few essential articles to revisit, updates to know about, and one question to ponder:

Three articles to revisit:

Two quick updates:

Did you know…

When using the @for block, you don’t need to declare local variables to use them. Just use $index, $even in your template, or any other local variable, and… it works!

@for (item of items; track item.id) {
<p>Item #{{ $index }}: {{ item.name }}</p>
}

Angular AI and debugging tools

This week, we’re using the 3-2-1 format of the newsletter. I’m posting a few essential articles to revisit, updates to know about, and one question to ponder:

Three articles to revisit:

Two quick updates:

  • Angular 21 is scheduled for the week of November 17th.
  • The Angular AI tutor is officially available! It’s an interactive AI-based tutorial that you can chat with and gives you challenges to work on.

One question to ponder:

Do you use any of the AI features recently released by the Angular team, such as the MCP server? If not, would you like me to write a few articles about AI with Angular? Please hit reply to let me know.

Server-side rendering: SSR or SSG, what’s the difference?

I’ve covered server-side rendering in the past, but recently realized that people get confused about SSR and SSG.

What’s the difference? SSR stands for Server-Side Rendering, and it does what it says: when requested, an Angular route is rendered on the server before being sent to the client as HTML syntax.

Then, the browser loads Angular’s JavaScript code, and the process of hydration starts: Angular takes over the “static” DOM built from our server and hooks its component code to it. In other words, front-end JavaScript takes over from that point on.

SSG stands for Static Site Generation, and the process is very much the same as SSR, with one significant difference: SSG is performed at build time, not at runtime. This is important for several reasons:

  1. The pre-rendered SSG pages can be generated weeks, if not months, before being displayed in a browser, which means they can display stale data. They don’t get updated after that last ng build, though Angular will take over and hydrate them on load, just like with SSR.
  2. SSG doesn’t require a Node.js server, but SSR does. The build output of SSG can be used as-is and deployed to any web server, like a regular client-side Angular app.

Now, how to choose between the two options? Well, the good news is that it doesn’t have to be the same choice for all routes. In an SSR app, a server-side router config is generated, and that file can be tweaked to do SSR, SSG, or even just client-side rendering based on the route:

The above example makes perfect sense: An about page doesn’t change between builds. It can be pre-rendered with SSG.

A profile page depends on user data, so this doesn’t make sense to pre-render at build time. SSR or client-side rendering are the two best options.

Last but not least, what if we want to pre-render routes that use parameters? We can do so by adding a list of param values to pre-render routes with the getPrerenderParams function:

The above example would have the Angular compiler pre-render the routes for /post/1/foo/3 and /post/2/bar/4 at build time.

Dates, pipes, and coupons!

This week, we’re using the 3-2-1 format of the newsletter again. I’m posting a few essential articles to revisit, updates to know about, and one question to ponder:

Three articles to revisit:

  • Still using Moment.js for date formatting? Take a look at date-fns as a possible alternative.
  • If you’re using the date pipe anywhere, including in your TypeScript code, knowing how to format dates without pipes might help.
  • Also, if you’re constantly using the same date format with your pipes, you can set the default format once for all, so you don’t need to repeat yourself in all your templates.

Two quick updates:

  • I’m speaking at the International JavaScript conference in New York City, and I’m pretty much running the entire Angular track at the event, with two talks and one full-day workshop. The event can also be attended remotely from home. As a reader of this email, you can get 10% off your conference ticket by using coupon code ijsny-alain. And we’re also throwing a certificates.dev coupon in a mix, you can get $25 Off ANY mid-level or senior certification using code IJCNY25.
  • Angular Signal Forms will officially become experimental in Angular 21, and the first documentation is now live! You can start exploring the new signal forms API to see how forms will work with Angular’s signal-based approach.

One question to ponder:

Are you ready for Angular 21? If not, what’s your biggest concern or impediment to upgrade? The introduction of experimental Signal Forms is just one of many changes coming that might reshape how we build Angular applications.

Signal Forms Preview + AI event

This week, we’re using the 3-2-1 format of the newsletter again. I’m posting a few essential articles to revisit, updates to know about, and one question to ponder:

Three articles to revisit:

Two quick updates:

  • Angular Signal Forms are coming soon, and Matthieu Riegler created a Stackblitz playground for you to explore the basics of the API with a simple example.
  • Next week, the Angular team has a live AI event on YouTube, free to sign up for and watch.

One question to ponder:

Do you use Reactive forms? If so, why did you choose them over template-driven forms? If you’re ready to change your mind or learn something new, this video from Ward Bell is really interesting.

ngModel, model(), and 2-way bindings in general

This week, we’re back to the 3-2-1 format of the newsletter. I’m posting a few essential articles to revisit, updates to know about, and one question to ponder:

Three articles to revisit:

Two quick updates:

One question to ponder:

What’s new in Angular 20.2?

Another month, another minor version of our favorite front-end framework!

Zoneless

This time around, Zoneless change detection becomes stable (more about Zoneless here), so you can remove Zone.js from the equation if you’ve followed the rules to make sure Angular will detect your changes properly with OnPush-compatible components.

Animations

The @angular/animations package is deprecated and replaced with a new API using the animate.enter/animate.leave syntax as follows:

 <div animate.enter="fade-in" animate.leave="fade-out">My content</div>Code language: HTML, XML (xml)

Aliases in blocks

You can use local aliases in if, else, and else if blocks as follows:


⁠@if (currentUser(); as user) {
  Welcome user {{ user.name }}!
} @else if (expiredSession(); as exp) {
  Please log in. Your session expired at {{ exp.time }}
}Code language: JavaScript (javascript)

New diagnostics

Diagnostics give you hints and feedback on possible mistakes. These apply to instances where your code compiles but is likely doing something wrong. A new diagnostic called uninvokedFunctionInTextInterpolation has been added to warn you when you use a method in a template without calling it (forgot the parentheses).

Testing

You can now use a headless browser with your Vitest unit tests using the builder introduced in v20. This is still experimental, though.

As a reminder, here are the updates from the previous version, Angular 20.1.