Angular forms are used to enable users to log in to the application, update a profile, enter sensitive information, and perform various data-entry tasks. In Angular 21, there are three approaches to building forms:
Using reactive forms, instead of defining the form in our template (i.e. in component.html), the structure of the form is defined in the component (i.e. component.ts).
Using template-driven forms, we first create HTML input elements in the template and then use directives like ngModel to bind their value to a component's variable.
Signal Forms are a new experimental approach via the @angular/forms/signals package, integrating deeply with Angular's Signals system for better type safety and simpler validation.
Both reactive and template-driven forms collect user input from the view, validate it, and track changes. For small projects, either approach works well. For larger projects, reactive forms are recommended as they scale better.
Create a component for reactive forms using the Angular CLI.
ng generate component reactive-form In Angular 21 standalone components, import ReactiveFormsModule directly in the component's imports array - no NgModule needed.
import { Component } from '@angular/core';import { ReactiveFormsModule, FormBuilder, Validators } from '@angular/forms';@Component({ selector: 'app-reactive-form', standalone: true, imports: [ReactiveFormsModule], template: ` <form [formGroup]="form" (ngSubmit)="onSubmit()"> <label> Name: <input formControlName="name" /> @if (form.get('name')?.invalid && form.get('name')?.touched) { <span style="color:red">Name is required</span> } </label> <br /> <label> Email: <input formControlName="email" type="email" /> @if (form.get('email')?.invalid && form.get('email')?.touched) { <span style="color:red">Valid email required</span> } </label> <br /> <button type="submit" [disabled]="form.invalid">Submit</button> </form> @if (submitted) { <p>Form submitted: {{ form.value | json }}</p> } `})export class ReactiveFormComponent { private fb = inject(FormBuilder); form = this.fb.group({ name: ['', Validators.required], email: ['', [Validators.required, Validators.email]] }); submitted = false; onSubmit() { if (this.form.valid) { this.submitted = true; } }}import { inject } from '@angular/core'; ng generate component template-form Import FormsModule directly in the standalone component's imports array to enable ngModel.
import { Component } from '@angular/core';import { FormsModule } from '@angular/forms';@Component({ selector: 'app-template-form', standalone: true, imports: [FormsModule], template: ` <form #f="ngForm" (ngSubmit)="onSubmit(f)"> <label> Name: <input name="name" ngModel required /> </label> <br /> <label> Email: <input name="email" ngModel required email type="email" /> </label> <br /> <button type="submit" [disabled]="f.invalid">Submit</button> </form> @if (result) { <p>Submitted: {{ result | json }}</p> } `})export class TemplateFormComponent { result: any; onSubmit(form: any) { this.result = form.value; }} Angular 21 introduces Signal Forms as an experimental new approach via the @angular/forms/signals package. Signal Forms integrate deeply with Angular's Signals system and provide better type safety and simpler validation compared to Reactive and Template-driven forms.
Explore 500+ free tutorials across 20+ languages and frameworks.