Fix: Can't Bind to 'formGroup' in Angular (Known Property)
TypeScript

Fix: Can't Bind to 'formGroup' in Angular (Known Property)

The 'Can't bind to formGroup' error occurs when Angular doesn't recognize the formGroup directive. Here's how to fix it in both NgModule and standalone setups.

2026-07-02
8 min read
Fix: Can't Bind to 'formGroup' in Angular (Known Property)

Photo by Chris Ried on Unsplash

Angular's compiler stops the build with this exact message the moment it meets <form [formGroup]="myForm"> in a template it can't resolve:

plaintext
Can't bind to 'formGroup' since it isn't a known property of 'form'.
If 'formGroup' is a directive, ensure it is included in the @NgModule imports or standalone component imports.

The cause is almost always a missing ReactiveFormsModule import — either in your NgModule or in the imports array of a standalone component. The behavior is the same across all Angular versions ≥ v2 — it's not version-specific, just configuration-specific. Where the import belongs, however, depends on your setup, and that's where the three common scenarios diverge:

  • a lazy-loaded module where ReactiveFormsModule isn't imported
  • a standalone component that doesn't list ReactiveFormsModule in its imports array
  • a shared module that declares a form component but forgets to re-export ReactiveFormsModule

Why the compiler rejects [formGroup]#

The formGroup directive is not part of Angular's core runtime — it's provided by the @angular/forms package and only becomes available when ReactiveFormsModule is imported. Angular's Ahead-of-Time (AOT) compiler enforces strict template type checking: if a directive isn't declared in the current module's imports array (or the standalone component's imports), the compiler treats the binding as an unknown property.

This is intentional — it prevents accidental use of directives that may not be available in production builds. The compiler doesn't know where formGroup comes from, so it flags it as invalid HTML.

Here's a component that triggers the error even though the TypeScript itself is correct:

ts
// src/app/app.component.ts
import { Component } from '@angular/core';
import { FormGroup, FormControl } from '@angular/forms';
 
@Component({
  selector: 'app-root',
  template: `
    <form [formGroup]="loginForm" (ngSubmit)="onSubmit()">
      <input formControlName="email" />
      <button type="submit">Login</button>
    </form>
  `
})
export class AppComponent {
  loginForm = new FormGroup({
    email: new FormControl(''),
    password: new FormControl('')
  });
}

Angular sees [formGroup] and says: "I don't know what formGroup is — it's not in my directive registry." The component code needs no change at all; the import declaration does.

NgModule apps: add it to the module's imports#

For NgModule-based apps (pre-v14 or hybrid):

ts
// src/app/app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { ReactiveFormsModule } from '@angular/forms';
 
import { AppComponent } from './app.component';
 
@NgModule({
  declarations: [AppComponent],
  imports: [
    BrowserModule,
    ReactiveFormsModule // ← add this
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }

That single change addresses the cause because ReactiveFormsModule registers the formGroup, formControlName, and formGroupName directives with Angular's DI system — making them available for template compilation.

In practice: open app.module.ts, locate @NgModule({ imports: [...] }), add ReactiveFormsModule to the array, then restart ng serve or run ng build to recompile.

Standalone components (Angular 14+): the import belongs on the component#

Standalone components don't inherit anything from an AppModule — each one declares its own dependencies in the @Component decorator:

ts
// src/app/login/login.component.ts
import { Component } from '@angular/core';
import { ReactiveFormsModule, FormGroup, FormControl } from '@angular/forms';
import { FormsModule } from '@angular/forms'; // only if you use ngModel
 
@Component({
  selector: 'app-login',
  standalone: true,
  imports: [
    ReactiveFormsModule // ← required for formGroup
    // optionally: FormsModule
  ],
  template: `
    <form [formGroup]="loginForm" (ngSubmit)="onSubmit()">
      <input formControlName="email" />
      <button type="submit">Login</button>
    </form>
  `
})
export class LoginComponent {
  loginForm = new FormGroup({
    email: new FormControl(''),
    password: new FormControl('')
  });
 
  onSubmit() {
    console.log(this.loginForm.value);
  }
}

Note: If your standalone component is used in a lazy-loaded module, you must import ReactiveFormsModule directly in the component — importing it in the lazy module alone won't work, because the component's scope is isolated.

The two configurations that still fail after the import#

If the error persists after adding the import, one of these two module-scoping traps is usually in play.

Lazy-loaded child module hosting a standalone component. If LoginComponent lives in LazyModule, and LazyModule imports ReactiveFormsModule, but LoginComponent is standalone and doesn't import ReactiveFormsModule in its @Component decorator, the error persists. Lazy modules don't automatically expose their imports to child standalone components — import ReactiveFormsModule directly in the standalone component.

Shared module without a re-export. If you declare a form component in a shared module and import ReactiveFormsModule there, but forget to re-export it, consumers of the shared module won't get the directives:

ts
// src/app/shared/forms.module.ts
import { NgModule } from '@angular/core';
import { ReactiveFormsModule } from '@angular/forms';
 
@NgModule({
  imports: [ReactiveFormsModule],
  exports: [ReactiveFormsModule] // ← critical for shared use
})
export class FormsSharedModule { }

Then import FormsSharedModule instead of ReactiveFormsModule directly in other modules.

Confirming the build compiles#

Run:

bash
ng build --configuration development

You should see:

plaintext
✔ Browser application bundle has finished.
✔ Compiling with Angular's incremental compiler (incremental mode).
✔ Build completed.

No compiler errors. The form renders, and formGroup binding works — you can type into the inputs and see this.loginForm.value update in the console.

FormsModule vs ReactiveFormsModule — why the split exists#

Reactive forms are opt-in — Angular deliberately separates FormsModule (for template-driven forms with ngModel) from ReactiveFormsModule (for model-driven forms with FormGroup, FormControl, etc.). This keeps the core bundle lean and avoids accidental mixing of patterns.

If you're mixing reactive and template-driven forms (e.g., using ngModel alongside formGroup), remember: FormsModule and ReactiveFormsModule can coexist, but both must be imported. Confusing the two is a common source of errors — see JSX.Element vs ReactNode vs ReactElement: TS2322 Fix for a parallel in React's type system, where missing imports cause similar "unknown property" errors.

To prevent regressions, run ng build in your CI pipeline — if ReactiveFormsModule is missing from the relevant imports array, the build will fail and you'll catch the issue before merge. You can also write a custom angular-eslint rule that flags components or modules using formGroup or formControlName without ReactiveFormsModule declared in their imports array.

I cover similar Angular type-checking pitfalls — like missing imports, incorrect module scope, and template binding errors — in TypeScript Getter Setter Errors: TS1056, TS1028, TS2378 Fix, where I explain how Angular's template compiler enforces strict contracts between directives and modules.

Frequently Asked Questions

|

Have more questions? Contact us

Written by

Mahdi Br
Mahdi Br

Full-Stack Dev — Next.js & Supabase

Solo developer building SaaS products with Next.js and Supabase. Writing about production patterns the official docs skip.

Remote

One email a month — no fluff

RLS gotchas, Next.js cache debugging, and the one Supabase setting that bit me last month.