Angular v21 — Tutorial: Signal Forms, Angular Aria, MCP & Zoneless (Hands-On)
A short, practical tutorial showing how to get started with the main Angular v21 features and small working examples you can paste into your project.
What you'll learn
- How to scaffold a new Angular v21 project (commands)
- A Signal Forms login form example
- Using an Angular Aria combobox (headless accessible UI)
- How to use the MCP server in read-only mode
- Notes on zoneless apps and Vitest
Prerequisites
Make sure you have:
- Node.js (>= 18 recommended)
- npm or yarn
- Angular CLI (latest)
- Basic TypeScript and Angular knowledge
Note: Replace `ng` commands with your preferred package manager if needed (npm/yarn).
1) Create a new Angular v21 project
Scaffold a fresh app (the CLI will create a project configured for v21 defaults):
npx @angular/cli@latest new angular-v21-demo
cd angular-v21-demo
npm install
If you already have an app, use ng update to migrate to v21.
2) Signal Forms — Example: Login form
Signal Forms let you manage form state with signals instead of FormGroup. Here's a minimal login form using the new API (example is simplified).
Install (if needed) — the forms signals API is provided from Angular forms package:
npm install @angular/forms
login.component.ts
import { Component } from '@angular/core';
import { signal } from '@angular/core';
import { form, Field } from '@angular/forms/signals';
@Component({
standalone: true,
selector: 'app-login',
imports: [Field],
templateUrl: './login.component.html'
})
export class LoginComponent {
// simple reactive model as a signal
model = signal({ email: '', password: '' });
// create a Signal Form instance bound to the model
loginForm = form(this.model);
onSubmit() {
// loginForm.value() or access model()
console.log('Submitting', this.model());
// send to server, show validation, etc.
}
}
login.component.html
<form (ngSubmit)="onSubmit()" >
<label>Email</label>
<input [field]="loginForm.email" type="email" />
<label>Password</label>
<input [field]="loginForm.password" type="password" />
<button type="submit">Sign in</button>
</form>
This approach reduces boilerplate and integrates validation in a predictable, type-safe way. For custom controls, signal bindings avoid many previous complexities around ControlValueAccessor.
3) Angular Aria — Example: Accessible Combobox
Angular Aria provides headless (unstyled) primitives built with ARIA and keyboard interactions in mind. Install and use them as primitives for your custom UI.
npm i @angular/aria
combobox.component.ts (simplified)
import { Component, signal } from '@angular/core';
/* pseudo-imports — follow official Aria docs for actual API names */
import { ComboboxDirective } from '@angular/aria';
@Component({
standalone: true,
selector: 'app-combobox',
template: `
<div aria-label="Country selector">
<input [ariaCombobox]="items" [value]="query()" (input)="onInput($event)" />
<ul role="listbox">
<li *ngFor="let it of filteredItems()" role="option">{{ it }}</li>
</ul>
</div>
`
})
export class ComboboxComponent {
query = signal('');
items = ['India','United States','Canada','Australia','Germany'];
filteredItems = () => this.items.filter(i => i.toLowerCase().includes(this.query().toLowerCase()));
onInput(e: Event) {
const v = (e.target as HTMLInputElement).value;
this.query.set(v);
}
}
Angular Aria ensures keyboard navigation, focus management and ARIA attributes are correct — you only style it to match your UI.
4) MCP Server — quick read-only example
The Model Context Protocol (MCP) server allows LLM agents to query your workspace. Run it in a safe read-only mode while experimenting:
# Start the MCP server in read-only mode (CLI)
ng mcp --read-only
An AI agent connected to MCP can query project structure, docs, or suggest migration steps. Keep the server in read-only for safety when experimenting.
5) Zoneless apps & Enabling zones (if needed)
By default v21 new projects are zoneless — Angular relies on Signals + explicit update triggers. This yields smaller bundles and more predictable rendering.
If you need Zone-based detection temporarily, add the provider:
import { provideZoneChangeDetection } from '@angular/core';
bootstrapApplication(App, {
providers: [ provideZoneChangeDetection() ]
});
Use migration schematics for large apps rather than flipping the provider manually across many modules.
6) Quick: Vitest example (test a service)
Angular v21 defaults to Vitest for new projects. Minimal example test for a simple service:
// greeting.service.ts
export class GreetingService {
greet(name: string) { return `Hello, ${name}`; }
}
// greeting.spec.ts (Vitest)
import { describe, it, expect } from 'vitest';
import { GreetingService } from './greeting.service';
describe('GreetingService', () => {
it('returns greeting', () => {
const svc = new GreetingService();
expect(svc.greet('Alice')).toBe('Hello, Alice');
});
});
Run tests with npm test or npx vitest depending on your setup.
7) Quick migration tips
- Use
ng updateto apply official migration schematics. - Try Signal Forms and Aria in a feature branch or isolated module first.
- Use the MCP tool
onpush_zoneless_migrationto generate migration suggestions. - Migrate tests to Vitest using the provided refactor schematic.
Final notes
Angular v21 is focused on modern reactive patterns, accessibility primitives, and AI-enhanced development workflows. Start small: adopt Signal Forms and Aria in a feature module, use MCP read-only to discover how AI can help your codebase, and migrate tests to Vitest for a faster developer experience.
If you'd like, I can:
- Convert any of the above examples into a downloadable StackBlitz-ready project
- Write a full migration checklist for a production app
- Create SEO-tailored headings and meta tags specifically for your Blogger theme
