Omnissa logo
Engineering
UI

Angular Signal Forms Series: Getting Started with Signal Forms

WWei Tian
December 2nd, 2025
Angular Signal Forms Series: Getting Started with Signal Forms

With the release of Angular 21 on Nov 20, Angular introduced the long-awaited Signal Forms, providing scalable, composable, and reactive forms built on Signals. Although Signal Forms are still experimental, let's take an initial look at them. Instead of building forms from scratch, you start with your data model and let Angular generate the form structure automatically.

Angular introduced Signals since v17 to help create and manage state. Signal Forms leverage the benefits of Signals to provide a reactive and type-safe way for building forms. The first PR introducing Signal Forms was merged on Aug 29, 2025. Further enhancements and improvements continued over the past 4 months. Let's have a glance at Signal Forms and see how form development has changed.

Why Signal Forms?

Pain Points of Traditional Reactive Forms

Traditional Reactive Forms have served Angular developers well for years, but several challenges become apparent as applications grow in complexity.

Pain Point 1: Lack of Type Safety

One of the most significant pain points is the weak type system. When you create a FormGroup, TypeScript does not enforce type safety between your form structure and your data model:

// Your data model
interface UserProfile {
  firstName: string;
  lastName: string;
  age: number;
}

// Your form - no type checking!
this.profileForm = new FormGroup({
  firstName: new FormControl(''),
  lastName: new FormControl(''),
  age: new FormControl('') // Should be a number, but nothing prevents strings
});

// This can lead to runtime errors
const age = this.profileForm.get('age')?.value; // Type is 'any'
const typo = this.profileForm.get('agee')?.value; // No error at compile time!

Pain Point 2: Boilerplate and Verbosity

Working with forms requires significant boilerplate code. For each form, you typically need to:

  1. Define the form structure with FormGroup and FormControl
  2. Initialize default values
  3. Map form values to your data model
  4. Handle value changes with subscriptions
  5. Manually clean up subscriptions to prevent memory leaks
export class UserProfileComponent implements OnInit, OnDestroy {
  profileForm: FormGroup;
  private destroy$ = new Subject<void>();

  ngOnInit() {
    // Lots of boilerplate just to create a form
    this.profileForm = new FormGroup({
      firstName: new FormControl('', [Validators.required]),
      lastName: new FormControl('', [Validators.required]),
      email: new FormControl('', [Validators.required, Validators.email]),
      age: new FormControl(null, [Validators.min(18)])
    });

    // Subscribe to changes
    this.profileForm.valueChanges
      .pipe(takeUntil(this.destroy$))
      .subscribe(value => {
        // Handle changes
      });
  }

  ngOnDestroy() {
    this.destroy$.next();
    this.destroy$.complete();
  }
}

Pain Point 3: Change Detection Performance

Traditional Reactive Forms rely on Zone.js for change detection, which can trigger unnecessary re-renders throughout your component tree. Every form value change can potentially trigger change detection for the entire application, leading to performance issues in large or complex applications.

Pain Point 4: State Management Complexity

Managing form state becomes especially challenging when dealing with:

Goals of Signal Forms

The Angular team designed Signal Forms with ambitious goals to address the shortcomings of traditional forms while embracing modern reactive patterns. Here are the key objectives:

Goal 1: Signal-Centric Design

Signals are placed at the core of the forms experience, enabling a truly reactive programming model for form state and logic. Instead of relying on observables and subscriptions, Signal Forms leverage the fine-grained reactivity that signals provide, automatically tracking dependencies and updating only what's necessary.

Goal 2: Declarative Logic

Allow developers to define form behavior—such as validation and conditional fields—declaratively using TypeScript. This approach moves logic out of the template and into a typed, testable schema. Rather than scattering form logic across templates and component methods, you can define everything in one place with complete type safety.

Goal 3: Developer-Owned Data Model

Signal Forms do not maintain a separate copy of data in a form model. Instead, they read and write directly via a developer-provided WritableSignal, eliminating the need for applications to synchronize their data with the form system. Your source of truth remains in your component or service, not hidden within form internals.

// Your data is the source of truth
const user = signal<User>({ name: '', email: '', age: 0 });

Goal 4: Interoperability

A key design goal is seamless interoperability with existing Reactive Forms, allowing incremental adoption. You don't need to rewrite your entire application at once. Signal Forms can coexist with traditional forms, enabling teams to migrate gradually without disrupting existing functionality.

Goal 5: Bridging Template and Reactive Forms

Signal Forms aim to close the gap between template-driven and reactive forms, offering a unified and more powerful approach that combines the best aspects of both. They provide the simplicity of template-driven forms with the power and flexibility of reactive forms, all while adding the benefits of signals.

Getting Started with Signal Forms

Signal Forms represent a paradigm shift in Angular form handling. At their core, Signal Forms replace the traditional FormControl and FormGroup with the signal-based Field directive, which directly integrates with Angular's reactivity system. Instead of manually creating form structures, you define your data model and let the framework generate type-safe form fields automatically.

The key innovation is that form state lives in signals, eliminating the need for subscriptions and manual change tracking. Every form field is a signal that automatically notifies dependents when values change, enabling truly reactive forms with minimal boilerplate.

Let's build a simple user profile form to see Signal Forms in action. We'll start with a basic example and progressively add features. The whole POC code can be found in this repository.

Step 1: Installation and Setup

Signal Forms require Angular 21 or later. Be sure to upgrade your version of Angular if necessary.

All the examples in this article use 21.0.0-rc.2. Here is an example of compatible dependency versions:

  "dependencies": {
    "@angular/animations": "21.0.0-rc.2",
    "@angular/common": "21.0.0-rc.2",
    "@angular/compiler": "21.0.0-rc.2",
    "@angular/core": "21.0.0-rc.2",
    "@angular/forms": "21.0.0-rc.2",
    "@angular/platform-browser": "21.0.0-rc.2",
    "@angular/platform-browser-dynamic": "21.0.0-rc.2",
    "@angular/router": "21.0.0-rc.2",
    "rxjs": "~7.8.0",
    "tslib": "^2.3.0",
    "zone.js": "~0.15.0"
  },
  "devDependencies": {
    "@angular-devkit/build-angular": "21.0.0-rc.1",
    "@angular/cli": "21.0.0-rc.1",
    "@angular/compiler-cli": "21.0.0-rc.2",
    "@types/jasmine": "~5.1.0",
    "jasmine-core": "~5.1.0",
    "karma": "~6.4.0",
    "karma-chrome-launcher": "~3.2.0",
    "karma-coverage": "~2.2.0",
    "karma-jasmine": "~5.1.0",
    "karma-jasmine-html-reporter": "~2.1.0",
    "typescript": "5.9.3"
  }

Step 2: Define Your Data Model

Signal Forms are model-first, so begin by defining the TypeScript interface for your data:

export interface SimpleUser {
  firstName: string;
}

Step 3: Create the Form with the form() Function

The form() function creates a type-safe form structure from your model. You can define validations inline using a declarative API:

public user = signal<SimpleUser>({
  firstName: '',
});
public userForm = form<SimpleUser>(this.user, (path) => {
  required(path.firstName, { message: 'First Name is required' });
});

Step 4: Bind Form Fields to Your Template

Field directive is used to bind form fields to template. To use it, first import Field to the component:

@Component({
  selector: 'app-basic-signal',
  imports: [
    CommonModule,
    Field,
    ReactiveFormsModule,
  ],
  templateUrl: './basic-signal.component.html',
  styleUrls: ['./basic-signal.component.scss'],
})
export class BasicSignalComponent {
}

Use the [field] directive to bind form fields directly to your inputs. No need for formControlName or complex bindings.

<input
  [field]="userForm.firstName"
  id="firstName"
  type="text"
  placeholder="Enter your first name">

Step 5: Access Field State with Signals

Every form field is a signal that provides rich state information. Call the field as a function to access its current state:

Example: Reactive Error Display

@if (userForm.firstName().touched()) {
  @for (error of userForm.firstName().errors(); track error) {
    <div class="error-message">
      {{ error.message }}
    </div>
  }
}

Step 6: Read Form Values

Access the entire form data through the value() signal:

// Get all form values as an object
const formData = this.userForm().value();
console.log(formData); // { firstName: 'Wei', lastName: 'Tian', email: 'wtian@omnissa.com' }

// Or access individual field values
const firstName = this.userForm.firstName().value();

Step 7: Handle Form Submission

The submit() function provides built-in loading states and error handling:

onSubmit(event: Event) {
  event.preventDefault();
  submit(this.userForm, (form: FieldTree<SimpleUser, string | number>) => {
    console.log('Submitted form data:', form().value());
    return lastValueFrom(of(undefined));
  });
}

The submit() function automatically:

Conclusion

Angular Signal Forms represent a significant leap forward in how forms are built and managed in Angular applications. By addressing the fundamental pain points of traditional Reactive Forms—weak type safety, excessive boilerplate, performance bottlenecks, and state management complexity—Signal Forms offer a modern, signal-centric approach that aligns with Angular's reactive future.

Key Takeaways

Type Safety First: Signal Forms provide end-to-end type checking from your data model to template bindings, catching errors at compile time instead of runtime. No more any types or unnoticed typos.

Less Code, More Clarity: The model-first, declarative approach dramatically reduces boilerplate code. Define your data model, add validations in one place, and let Angular handle the rest—no more subscription management or manual state synchronization.

Performance by Design: Leveraging signals' fine-grained reactivity, Signal Forms update only what's necessary, eliminating Zone.js overhead and improving performance in large applications.

Developer Experience: From the intuitive form() function to the simple [field] directive and built-in submit() handler, Signal Forms streamline the entire form development workflow while maintaining control and flexibility.

Should You Use Signal Forms?

While Signal Forms are currently experimental in Angular 21, they show tremendous promise. If you're starting a new project or have forms that would benefit from improved type safety and simpler code, Signal Forms are worth exploring. For existing applications, seamless interoperability with Reactive Forms means you can adopt Signal Forms incrementally without a complete rewrite.

However, keep in mind:

What's Next?

In this article, we've covered the fundamentals of Signal Forms and how to get started with basic form creation and validation. In the next blog post, I'll dive deeper into the advanced features of Signal Forms, including:

Stay tuned to explore the full power of Signal Forms and learn how to leverage them in complex, production-ready Angular applications!

Resources and Further Reading

Happy coding with Signal Forms!