Angular 21 has officially landed. Three additions deserve special attention: @angular/aria, the adoption of Vitest as the stable testing solution, and the showstopper — Signal Forms.
Signal Forms
The arrival of Signal Forms means we finally get a single unified forms module, taking over from both the Reactive and Template-Driven approaches. That said, the previous modules remain fully supported and are not marked as deprecated.
Just about every piece of state within Signal Forms is represented as a Signal — from the individual field and its value to the overall status and any error messages.
import { Component, signal } from '@angular/core';
import { Field, form, required } from '@angular/forms/signals';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';
@Component({
template: `<form (submit)="$event.preventDefault()">
<mat-form-field>
<mat-label>Firstname</mat-label>
<input type="text" matInput [field]="userForm.firstname" />
</mat-form-field>
<mat-form-field>
<mat-label>Lastname</mat-label>
<input type="text" matInput [field]="userForm.lastname" />
</mat-form-field>
<button [disabled]="userForm().invalid()">Submit</button>
</form>`,
imports: [Field, MatFormFieldModule, MatInputModule],
})
export class NgNews {
readonly #user = signal({
id: 1,
firstname: 'John',
lastname: 'Smith',
});
protected readonly userForm = form(this.#user, (path) => {
required(path.firstname);
required(path.lastname);
});
}
Using Signal Forms is straightforward. You start with a Signal that holds an object literal, which serves as the model you write into. Then, you invoke the form function, supply that Signal as the first argument, and the second argument becomes a callback where you specify your validation logic.
Within the template, standard HTML is used, and the sole directive you need to apply is called field—it must be added to each form control.
That’s all there is to it. The old form modules applied directives invisibly to form elements, input tags, and even HTML attributes, but that magic is gone. Now, only the field directive is explicit, while everything else remains plain HTML.
One consequence is that a form submission will trigger a full page reload, so you need to prevent the submit event from doing so.
Signal Forms are currently in an experimental release state, though certain comments suggest that a move to developer preview could happen in the near future.
@angular/aria
There's a clear shift underway, moving away from UI components that come pre-styled and ready for immediate use, toward a different approach: a collection of bare-bones parts that give you a robust starting point, and then you build your own custom look on top of that.
Angular Aria essentially follows this same philosophy, though it pushes it further—it hands you solely the accessibility groundwork, leaving every other aspect for you to shape.
Naturally, Angular Material and CDK remain options, yet they carry stronger stylistic opinions by default.
Vitest
Angular now adopts Vitest as its default testing framework. Here's a clarification to avoid any mix-ups: a fresh project created via ng new is set up with Vitest. However, if you are already on an existing project and run ng update, your current testing framework remains untouched. Still, there's an available migration script to assist with transitioning from Jasmine to Vitest if you wish.
While the overall APIs of Vitest and Jasmine are quite comparable, the key differences surface in mocking dependencies, especially when it comes to handling time. Jasmine uses its own spies, but Vitest provides similar functionality through vitest.fn. When dealing with time, Jasmine relies on jasmine.clock, whereas Vitest offers useFakeTimers() for controlling fake timers.
Let’s look at a Jasmine example:
import { TestBed } from '@angular/core/testing';
const dummyUser = { id: 1, name: 'John Smith' };
// Testing Example in Jasmine
describe('Ng-News Showcase', () => {
it('should mock SecurityService', () => {
const securityService = {
get: jasmine.createSpy(),
};
securityService.get.and.returnValue(dummyUser);
TestBed.configureTestingModule({
providers: [{ provide: SecurityStore, useValue: securityService }],
});
});
it('should fake the time', () => {
const clock = jasmine.clock();
clock.install();
let a = 1;
setTimeout(() => (a += 2), 1000);
expect(a).toBe(1);
clock.tick(1000);
expect(a).toBe(2);
clock.uninstall();
});
});
And here the same in Vitest:
import { TestBed } from '@angular/core/testing';
import { describe, it, expect, vitest } from 'vitest';
const dummyUser = { id: 1, name: 'John Smith' };
// Testing Example in Vitest
describe('Ng-News Showcase', () => {
it('should mock SecurityService', () => {
const securityService = {
get: vitest.fn(),
};
securityService.get.mockReturnValue(dummyUser);
TestBed.configureTestingModule({
providers: [{ provide: SecurityStore, useValue: securityService }],
});
});
it('should fake the time', () => {
vitest.useFakeTimers();
let a = 1;
setTimeout(() => (a += 2), 1000);
expect(a).toBe(1);
vitest.advanceTimersByTime(1000);
expect(a).toBe(2);
vitest.useRealTimers();
});
});
While the underlying principles are comparable, you should prepare to handle the migration of your tests manually in most cases.
The Angular CLI provides the wrapper around Vitest. Consequently, various configuration options, plugins, toolsets, and even IDE support may not be fully compatible. When in doubt, refer to the official Angular documentation.
Additionally, neither fakeAsync nor waitForAsync are supported here. Instead, you'll need to rely on Vitest's useFakeTimers or expect.poll as replacements.
To take advantage of everything Vitest has to offer, you'd need to turn to Analog's Vitest integration—the same one Nx uses under the hood.
Further Features
Resources continue to carry the experimental label.
On top of that, Angular 21 ships enhancements to its MCP server, plus numerous additional updates.
Community contributions—especially surrounding Signal Forms—remain plentiful, yet the official Angular blog should be your starting point.
https://blog.angular.dev/announcing-angular-v21-57946c34f14b
Don’t miss the Angular 21 release video either, in which the team demonstrates new functionality through a gaming theme reminiscent of Mario Land 3.
