For a new Angular 22 registration flow, would you choose Signal Forms or reactive forms, and which trade-offs decide it?
answer
- where the truth lives
- types inferred vs declared
- signals vs valueChanges
- interop in both directions
- plain-object models only
basics
~20 sFor a new signal-based Angular 22 app, Signal Forms: public API since v22, one signal model, inferred types, rules in one schema. Reactive forms stay the better fit for existing reactive codebases and valueChanges-heavy logic.
solid answer
~40 sI would start a new Angular 22 registration flow on Signal Forms, which became public API in v22.0: the data stays in one `signal()` model, field types are inferred from it, rules sit together in the schema (including `validate`, async checks and conditional `when`), and every piece of state is a signal, which fits zoneless and `OnPush`-by-default components. I would stay on reactive forms where the codebase already standardises on `FormGroup`, where logic leans on `valueChanges` Observable pipelines, or where the team is not yet fluent with signals. The choice is not all-or-nothing: `FormValueControl` components work with reactive and template-driven directives, and `[formField]` can bind existing `ControlValueAccessor` components. The costs to accept: plain-object models only, constraints expressed in the schema rather than template attributes, and a few members still marked experimental.
go deeper
Know that Angular 22 has Signal Forms alongside reactive and template-driven forms, and that Signal Forms keep the data in a signal model.
Compare where the data lives, how types are obtained and how validation is declared in Signal Forms versus reactive forms.
Make a recommendation for a concrete project and justify it with codebase state, interop paths, model constraints and the v22 stability status.
Plan an organisation-wide forms strategy: when new work defaults to Signal Forms, how shared controls migrate, and how long both systems coexist.
## Frame the decision Angular 22 ships three form systems: template-driven, reactive and Signal Forms. For a **new** registration flow the realistic choice is between reactive forms, the long-standing standard, and Signal Forms, which shipped experimental in v21 and became **public API in v22.0**. The Angular docs frame it the same way: Signal Forms for new signal-based applications; reactive forms for existing reactive codebases and teams that want the most established option. ## Where they differ | Concern | Signal Forms | Reactive forms | |---|---|---| | source of truth | your `signal()` model | the `FormGroup`/`FormControl` tree | | typing | inferred from the model | declared per control (typed forms) | | validation | path-based rules in one schema function | validator lists attached per control | | state | signals (`valid()`, `errors()`, `touched()`) | properties plus `valueChanges`/`statusChanges` Observables | | async checks | `validateHttp()` / `validateAsync()` over resources | `AsyncValidatorFn` returning Observable or Promise | | submission | `submit()`, `FormRoot`, server errors routed to fields | hand-written | | custom controls | `FormValueControl` (signal inputs, `model()`) | `ControlValueAccessor` | ## Arguments for Signal Forms here - **One model.** A registration flow is mostly a data object sent to the server; keeping it in a signal means the payload is `model()`, with no mapping from a control tree. - **Inferred types.** Renaming a field in the model breaks the template bindings at compile time. - **Centralised rules.** Cross-field (`validate` with `valueOf`), conditional (`required(path.x, {when})`, `applyWhen`) and async rules live together, with messages next to rules. - **Fits current defaults.** Components are `OnPush` by default since v22 and applications zoneless since v21; field state as signals refreshes exactly the views that read it, with no subscriptions to manage. - **Built-in submission.** `submit()` handles touch-all, gating, `submitting()`, double-submit protection and field-targeted server errors. ## Arguments for reactive forms - **An existing codebase** built on `FormGroup`, shared validators and helper libraries; two form systems side by side cost onboarding and review time. - **Observable-centred logic**, for example complex `valueChanges` pipelines with RxJS operators feeding other streams. - **Team fluency** and the length of its production record. - **Model constraints** of Signal Forms: the model must be plain objects and arrays. Class instances lose their prototype on the first write, `Map` and `Set` produce empty field trees, and native text inputs need `''` rather than `null`. ## Migration is gradual, in both directions - A `FormValueControl` component can be bound by `[formControl]`, `formControlName` and `ngModel` without compatibility code, so a component library can move first. - `[formField]` can bind an existing `ControlValueAccessor` component for backward compatibility. - `compatForm()` from `@angular/forms/signals/compat` lets a signal form contain existing reactive `FormControl`s (top-down migration), and `SignalFormControl` puts a signal-backed control inside an existing `FormGroup` (bottom-up). ## Pitfalls to name in the interview 1. **Template attributes move into the schema.** `required`, `[disabled]` or `[value]` on an element with `[formField]` is a template type-check error; `required()` and `disabled(path, {when})` are the replacements. 2. **Native constraint validation is not used**: `FormRoot` sets `novalidate`, and CSS such as `:invalid` is not a supported signal of validity. 3. **Experimental corners remain**: a few members still carry `@experimental` in v22, so check the API page before depending on an edge feature. ## Questions to settle before choosing 1. **How much reactive-forms code already exists**, and will the new flow share controls or validators with it? 2. **Does the flow rely on Observable pipelines** off form values, or can the logic be expressed with `computed()` and schema rules? 3. **Is the domain model made of classes**, and who owns the translation to a plain form model? 4. **Which shared controls exist**, and are they `ControlValueAccessor` components that `[formField]` must bind for now? 5. **Does the team's lint and review setup** already cover signals, `OnPush` and zoneless patterns? ## A defensible answer "For a new Angular 22 app built with signals, Signal Forms, because the model, types and rules each live in one place and state is already signals. In an existing reactive-forms codebase, reactive forms, and I would introduce Signal Forms control by control through `FormValueControl`, not by rewriting everything at once."
- What breaks if the registration model holds an Address class instance with a format() method?Signal Forms shallow-copies parent objects when it writes a field, so after the first edit the address is a plain object: its prototype, `format()` and `instanceof` checks are gone. Translate domain classes to plain objects at the form boundary and back on submit.
- How would you migrate a large reactive-forms codebase without a big-bang rewrite?Start with the shared controls: reimplement them as `FormValueControl` components, which reactive and template-driven forms can still bind. Then build new forms with `form()`, and move existing forms one at a time when they are next changed, using `compatForm()` or `SignalFormControl` from `@angular/forms/signals/compat` where a form must mix both systems.
saying these in an interview costs you the question
- Signal Forms is still experimental in Angular 22, so it cannot be used in production
- Choosing Signal Forms requires rewriting every ControlValueAccessor first
- Signal Forms uses the browser's constraint validation under the hood
- Any object, including class instances and Maps, works as a Signal Forms model
- Reactive forms are deprecated in Angular 22