The #Angular team is brewing something exciting in the experimental labs — Signal Forms. While still in its early PoC (Proof of Concept) stage, this innovative feature is shaping up to redefine how we approach forms in Angular😉.

Angular Signal Forms: Experimental (Your data model is the single source of truth)
Let’s kick things off with the basics of Signal Forms: the form() function and the powerful Field object — the heart of this new reactive paradigm.
The Main Idea: You Own the Data
At the core of Angular Signal Forms is a simple but powerful philosophy:
The library does not manage the form state — you do.
Your data model is the single source of truth, managed via Angular’s WritableSignal, and the form fields directly reflect this signal. The new API doesn’t introduce a parallel state system — it fully leans into the reactive capabilities of signals.
const userModel = signal<User>({ fullName: '', email: '', password: '', agreeToTerms: false});
The form() Function
To create a Signal Form, you call the form() function with your model:
const userForm: Field<User> = form(userModel);
This does two things:
- Creates a root Field object representing the form.
- Automatically generates nested Field objects for each key in the data model.
What’s more:
- Any updates made to the model (
userModel) immediately reflect in the form fields. - Any changes the user makes via the
userFormfields will directly mutate the underlying model.
This two-way reactive binding is what makes Signal Forms elegant and intuitive.
The Field Object
The Field object is the cornerstone of Signal Forms. Every form is essentially a root Field, and each property of your model becomes a sub-Field.
Each Field carries a special property called $state, which encapsulates its internal status via 5 signals:
FieldState Signals
userForm.email.$state.value(); // ''
value: AWritableSignal<T>representing the current field value.valid: ASignal<boolean>indicating if the field (and its children) are valid.errors: ASignal<FormError[] | undefined>with errors of the form{ kind: string, message?: string }.disabled: ASignal<boolean>indicating if the field or any ancestor is disabled.touched: ASignal<boolean>showing if the user has interacted with the field.
These signals give you full control to:
- Update values,
- Show validation messages,
- Handle disabled states,
- Track user interaction.
Navigating Fields
You can access deeply nested fields just like accessing properties in your object:
const emailField = userForm.email;const passwordField = userForm.password;const termsField = userForm.agreeToTerms;
This feels intuitive and mirrors your model’s structure exactly — no abstractions, no indirection.
Signal Forms in a Nutshell
- Use
form()on a signal model to create your form. - Model and UI stay in sync, thanks to writable signals.
- Each form element is a
Field, which exposes$statewith: value,valid,errors,disabled,touched- The form’s structure mirrors your data model — it’s predictable and type-safe.
Building a Deeply Nested Form with Signal Forms:
Angular’s new Signals combined with the experimental Signal Forms API provide a powerful way to build reactive forms with deep nested data structures — including arrays! In this guide, we’ll walk through creating a standalone component that manages a user profile form with nested skills and experience arrays, fully typed and reactive.
⚠️ Reminder: Signal Forms are still experimental and only available in the
prototype/signal-formsbranch of Angular. You need to clone and build Angular from that branch to run this.
Define the Data Model (Nested Fields)
We start by defining interfaces that represent the shape of our data. The UserProfile contains arrays of Skill and Experience:
interface Skill { name: string; level: string; // e.g. Beginner, Intermediate, Expert}interface Experience { company: string; role: string; years: number;}interface UserProfile { name: string; bio: string; skills: Skill[]; experience: Experience[];}
Create a Signal to Hold the Data (Nested Fields)
We initialize a writable signal with the initial user profile data:
profileModel = signal<UserProfile>({ name: '', bio: '', skills: [{ name: '', level: '' }], experience: [{ company: '', role: '', years: 0 }]});
Build a Nested Signal Form (Nested Fields)
Using Angular’s form() method, we define validations for each field. For arrays, we use $each to apply validators to every element:
profileForm: Field<UserProfile> = form(this.profileModel);
Access Nested Fields Easily
You can access deeply nested fields using array indexes, for example:
const firstSkillNameField = this.profileForm.skills[0].name;const firstExperienceCompanyField = this.profileForm.experience[0].company;
Angular’s new Signals combined with the experimental Signal Forms API provide a powerful way to build reactive forms with deep nested data structures — including arrays! In this guide, we’ll walk through creating a standalone component that manages a user profile form with nested skills and experience arrays, fully typed and reactive.