Angular Development Recipes
PLAngular development recipes for AI agents work when three things hold: the agent knows it is writing Angular 22 (zoneless, OnPush by default, Signal Forms stable), it can query the Angular CLI MCP server instead of guessing, and every change passes ng build, ng test on Vitest and a Playwright check.
This page is for developers who let Claude Code, Codex or Cursor write Angular code. The failure is familiar: the agent produces a component that compiles, the spec passes, and the view never updates, because the agent mutated a plain field the way Zone.js-era Angular allowed, and your app has no Zone.js to notice. Or it hands you BehaviorSubject services, *ngIf, @HostListener and a fakeAsync test that cannot run under Vitest.
What these Angular recipes give you
Section titled “What these Angular recipes give you”- A one-command setup that gives the agent Angular’s own best-practice file and the Angular CLI MCP server
- Ten copy-paste prompts for current Angular: components, services, Signal Forms, interceptors, routing, NgRx SignalStore, SSR, a zoneless migration, micro-frontends and directives
- A table of the gates that sign an Angular change off without anyone reading every line
- The Angular-specific ways agent code breaks, and how to recover from each
Which Angular 22 defaults do agents get wrong?
Section titled “Which Angular 22 defaults do agents get wrong?”Agents were trained on a decade of NgModule, Zone.js and decorator-era code. The current APIs and defaults below contradict most of that training data:
| Agent writes | What Angular 22 does | Write instead |
|---|---|---|
| A plain class field mutated in a handler, expecting the view to refresh | New CLI apps are zoneless (the application schematic’s zoneless option defaults to true), and ChangeDetectionStrategy.OnPush is the default strategy | A signal() or computed() read in the template |
changeDetection: ChangeDetectionStrategy.Default | Default is deprecated in favour of Eager | Leave it out; add Eager only with a written reason |
provideExperimentalZonelessChangeDetection() | Not exported by @angular/core 22.2.0 | provideZonelessChangeDetection(), or nothing in a new app |
FormGroup plumbing for a new form | Signal Forms (@angular/forms/signals: form(), [formField]) are public API since 22.0 | Signal Forms; typed reactive forms for existing forms |
fakeAsync and tick() in specs | ng test runs Vitest by default, and loads zone.js/testing only when the app’s polyfills include zone.js | Vitest fake timers (vi.useFakeTimers()) or await fixture.whenStable() |
provideClientHydration(withIncrementalHydration()) | Incremental hydration is on by default; withIncrementalHydration() is deprecated since 22.0 | provideClientHydration(), plus withEventReplay() if you want it |
@HostListener, @HostBinding, ngClass, CommonModule | Angular’s own AI instructions ban them for new code | The host object in the decorator, class and style bindings, per-directive imports |
The next section puts these rules into the agent’s context so you do not have to repeat them in every prompt.
Give the agent Angular’s own context and MCP server
Section titled “Give the agent Angular’s own context and MCP server”The Angular CLI ships two things written for coding agents. The ai-config schematic writes Angular’s best-practice instruction file and an MCP config for your tool. The ng mcp command runs the Angular CLI MCP server, whose default tools include get_best_practices, search_documentation, list_projects, run_target (build and test targets), dev-server start, stop and wait tools, and onpush_zoneless_migration.
Generate CLAUDE.md and a project .mcp.json from the repository root:
npx ng generate ai-config --tool=claude-codeIf a CLAUDE.md already exists, the schematic (checked in @schematics/angular 22.2.0) leaves it untouched and prints a warning, while an existing .mcp.json gets the angular-cli server merged into its mcpServers. To take Angular’s guidance into your own file, move CLAUDE.md aside, run the command, and merge the two by hand. To add only the MCP server, at project scope so the team shares it:
claude mcp add --scope project angular-cli -- npx -y @angular/cli mcpGenerate AGENTS.md and .codex/config.toml:
npx ng generate ai-config --tool=open-ai-codexOr add only the server to your user config:
codex mcp add angular-cli -- npx -y @angular/cli mcpCodex reads project-level AGENTS.md only in trusted projects (since 0.150.0), so trust the workspace when Codex asks.
Generate AGENTS.md (Angular’s best-practice file) and .cursor/mcp.json:
npx ng generate ai-config --tool=cursorThen open Cursor’s MCP settings, confirm angular-cli is listed and enabled, and copy the best-practice text from the generated AGENTS.md into a project Rule so every agent session loads it.
Add the project facts the schematic cannot know, below the generated block:
## This repository- Angular 22.2, standalone only, zoneless. Tests: `npx ng test --watch=false` (Vitest). Lint: `npx ng lint`.- State: signals in components; NgRx SignalStore (`@ngrx/signals` 22) for shared feature state.- New forms use Signal Forms. Never add `zone.js`, `fakeAsync`, `@HostListener` or `CommonModule`.- Before you finish: `npx ng build` and `npx ng test --watch=false` must both pass. Paste their last lines.The ng build line matters most. The production build type-checks every template, so a wrong input name or a missing import fails there, not in review. For the reasoning behind project context files, see how to make a codebase agent-ready.
How to run a recipe in each tool
Section titled “How to run a recipe in each tool”The prompts are tool-agnostic. What differs is how each tool runs the verification loop.
Paste the prompt into an interactive session, where auto mode is the starting permission mode from v2.1.283 (both release channels on 2026-10-12). To run a recipe headless in a script, allow only the commands it needs:
claude -p "$(cat prompts/angular-product-card.txt)" \ --permission-mode acceptEdits \ --allowedTools "Bash(npx ng build *)" "Bash(npx ng test *)"Paste the prompt into the TUI, or run it non-interactively with write access limited to the workspace:
codex exec --sandbox workspace-write "$(cat prompts/angular-product-card.txt)"For a long refactor such as the zoneless migration, codex cloud (experimental in 0.157.1) lets you browse a cloud task and apply its diff locally; codex apply applies the latest diff as a git apply.
Paste the prompt into the agent chat with the target folder and the relevant data-model or type file (for example src/app/models/product.ts) attached as context. For recipes that touch many files, such as routing or SSR, start in Plan Mode and approve the plan before any code is written. Review the diff in the editor, and keep the build and test commands in the prompt so the agent runs them in the terminal before it stops.
Build a standalone component with signals
Section titled “Build a standalone component with signals”Every prompt in this section names the file, the signal APIs and the proof you expect back. The 2025 file-name style (product-card.ts, not product-card.component.ts) is the CLI default in 22.x.
What proves it: the spec drives inputs through setInput() and asserts rendered text, so it fails if the template reads a stale field. A spec that only calls component methods proves nothing about change detection.
Replace BehaviorSubject services with signals and httpResource
Section titled “Replace BehaviorSubject services with signals and httpResource”resource() and httpResource() are public API since 22.0, and the CLI’s own guidance prefers inject() and the @Service decorator for new singletons. Keep RxJS where you have real streams; move state to signals.
Build a registration form with Signal Forms
Section titled “Build a registration form with Signal Forms”Signal Forms replace FormGroup wiring with a signal model, a schema function and the [formField] directive. The validators you need (required, email, minLength, pattern, validate, validateAsync, validateHttp, applyWhen, hidden, disabled) are all exported from @angular/forms/signals in 22.2.0.
For an existing reactive form, do not ask for a rewrite in the same change. Ask for typed NonNullableFormBuilder fixes first, then migrate one form per pull request with its tests unchanged, so the tests become the oracle for the migration.
Chain functional HTTP interceptors
Section titled “Chain functional HTTP interceptors”The concurrent-401 test is the one that catches real bugs. Agents write a refresh that works for a single request and fires three refreshes when a dashboard loads three widgets.
Lazy-load routes with guards and resolvers
Section titled “Lazy-load routes with guards and resolvers”The last line turns “is it really lazy?” into evidence. If the admin components appear under initial chunks, the prompt failed, whatever the diff says.
Manage feature state with NgRx SignalStore
Section titled “Manage feature state with NgRx SignalStore”Add SSR with hydration
Section titled “Add SSR with hydration”ng add @angular/ssr generates a server built on AngularNodeAppEngine and server routes with a RenderMode per path. Hydration comes from provideClientHydration(), which in 22.x already turns on incremental hydration and the HttpClient transfer cache; withEventReplay() is the option you add. withIncrementalHydration() is deprecated since 22.0, and the cache means responses fetched during server rendering are reused on the client, so do not ask the agent for hand-written TransferState code for plain HttpClient calls.
Migrate a legacy app to zoneless and OnPush
Section titled “Migrate a legacy app to zoneless and OnPush”This is the recipe where the Angular CLI MCP server earns its place: its onpush_zoneless_migration tool analyses a file for Zone.js-dependent patterns and returns a migration plan the agent then applies. Run it one feature folder at a time.
Freezing the assertions is what makes this safe to accept without reading every line: the existing tests are the specification, and the agent may not move them. See protect the test oracle for the general pattern, and legacy modernization for sequencing a larger migration.
Split a shell into micro-frontends with Native Federation
Section titled “Split a shell into micro-frontends with Native Federation”The CLI’s default application builder is esbuild-based. @angular-architects/module-federation targets webpack, so a current CLI app that keeps the default builder uses @angular-architects/native-federation instead, which delegates to the esbuild application builder.
Before you split, check that you need independent deployment. Micro-frontends trade build simplicity for team autonomy; microservices trade-offs apply to the frontend too.
Build directives with the host object
Section titled “Build directives with the host object”How to verify agent-written Angular code without reading every line
Section titled “How to verify agent-written Angular code without reading every line”Each gate answers one question, and together they replace a line-by-line read. The agent runs build and tests before it stops; CI runs all of them.
| Gate | Command | What it proves |
|---|---|---|
| Build with template type-checking | npx ng build | Templates bind to real inputs and outputs, imports are complete, budgets hold (angular.json → budgets) |
| Unit and component tests | npx ng test --watch=false | Behaviour through the DOM, on Vitest, with no Zone.js crutch |
| Lint | npx ng lint after npx ng add angular-eslint | Angular-specific TypeScript and template rules, including the template-accessibility config |
| End-to-end | npx playwright test | Real routing, lazy chunks, hydration and forms in a browser. See end-to-end testing with agents and Playwright MCP |
| Accessibility | Axe checks inside the Playwright suite, after npm i -D @axe-core/playwright (4.13.0 on 2026-09-26) | WCAG AA, which Angular’s own AI instructions require. See accessibility testing |
You still decide two things: that the acceptance criteria in the prompt are the right ones, and that no test assertion was weakened to get to green. A reviewer who reads the spec diff and the gate output, not the implementation diff, signs off. Bundle the outputs into the pull request as described in the evidence bundle.
When agent-written Angular code breaks
Section titled “When agent-written Angular code breaks”| Symptom | Cause | Recovery |
|---|---|---|
| The view does not update after a click or an HTTP response | A plain field mutated in a zoneless, OnPush app | Ask the agent to move template state to signals. Do not add zone.js back to hide it |
fakeAsync throws “zone-testing.js is needed for the fakeAsync() test helper” | Zone-era spec running under Vitest in a zoneless app | Rewrite the spec with vi.useFakeTimers() or await fixture.whenStable() |
ng build fails with “is not a known element”, “Can’t bind to … since it isn’t a known property” or “No pipe found with name” | A component, pipe, or directive used through a property binding is missing from the standalone component’s imports | Make the agent run ng build before it stops and fix every error; this case cannot reach a green build |
| An attribute directive silently does nothing, with no build error | The directive is missing from imports, and the template uses it only as a plain attribute or an event binding such as (clickOutside), which Angular compiles as an ordinary DOM attribute or listener | Require a spec that asserts the directive’s effect (for example, that the output fires), and check the imports array in the diff |
Resolved data or route params arrive as undefined inputs | withComponentInputBinding() is missing from provideRouter | Add it once in app.config.ts |
ReferenceError: window is not defined during SSR | Browser API read at construction time | Move the code into afterNextRender() or a platform-guarded service |
The agent installs @angular-architects/module-federation in an esbuild app | Webpack-era training data | Switch to Native Federation, or keep webpack deliberately and say so in the context file |
| Memory grows on long sessions | subscribe() without takeUntilDestroyed(), or an observer never disconnected | Grep the diff for subscribe( and require a destroy test for each |
When the agent keeps repeating the same wrong pattern, fix the context file, not the prompt: add the rule to the repository section of CLAUDE.md or AGENTS.md, or to your Cursor Rule, so the next session starts correct.
Where to go next with Angular and agents
Section titled “Where to go next with Angular and agents”- Frontend recipes overview for the other frameworks
- React development recipes to compare the same tasks in React
- API development patterns for the backend your Angular app calls
- Unit testing with agents for writing specs that fail for the right reasons
- Acceptance criteria for agents for turning a ticket into the criteria these prompts end with
Did this guide help you?
One tap, no sign-in.
What will you do with it?
How did it read?
What got in the way? Pick any that apply
Your role?
Thanks. I read every answer. Michał Jaskólski
Did this guide help?
One tap each. Close whenever you like.
no sign-in