Fix Can't bind to 'formGroup' since it isn't a known property of 'form'
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.
TL;DR#
If you're seeing Can't bind to 'formGroup' since it isn't a known property of 'form', the cause is almost always a missing ReactiveFormsModule import — either in your NgModule or in the imports array of a standalone component. Fix it by importing ReactiveFormsModule from @angular/forms and adding it to the appropriate imports array.
If that doesn't work, scroll to verify the fix — there are two common variants this guide also covers: child components in lazy-loaded modules and incorrect template syntax.
What you'll see#
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.It happens when Angular’s compiler processes your template and encounters <form [formGroup]="myForm">, but ReactiveFormsModule hasn’t been imported into the current module or component scope. The behavior is the same across all Angular versions ≥ v2 — it’s not version-specific, just configuration-specific.
The error appears in three common scenarios:
- In a lazy-loaded module where
ReactiveFormsModuleisn’t imported - In a standalone component that doesn’t list
ReactiveFormsModulein itsimportsarray - In a shared module that declares a form component but forgets to re-export
ReactiveFormsModule
Root cause#
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.
The relevant code path is:
// 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('')
});
}This component fails at compile time because ReactiveFormsModule is not imported — even though the component code itself is correct. Angular sees [formGroup] and says: “I don’t know what formGroup is — it’s not in my directive registry.”
The fix is simple: import ReactiveFormsModule and add it to the imports array. But where? That depends on whether you’re using NgModule or standalone components.
The fix#
Here’s the minimal change that resolves it for both setups.
For NgModule-based apps (pre-v14 or hybrid)#
// 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.
For standalone components (Angular 14+)#
// 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.
Step by step#
- Open
app.module.ts(for NgModule) or the standalone component file (e.g.,login.component.ts). - Locate the
importsarray — in@NgModule({ imports: [...] })or@Component({ imports: [...] }). - Add
ReactiveFormsModuleto that array. - Save and restart
ng serveor runng buildto recompile.
Verify the fix#
Run:
ng build --configuration developmentYou should see:
✔ 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.
If you’re still seeing the error, two common variants exist:
Variant A — Form used in a lazy-loaded child module#
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.
Fix: Import ReactiveFormsModule directly in the standalone component — lazy modules don’t automatically expose their imports to child standalone components.
Variant B — Using formGroup in a shared component without 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.
Fix: Add ReactiveFormsModule to the exports array of your shared module:
// 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.
Why this happens (and how to avoid it next time)#
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.
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.
Also, 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.
Related#
- Fix TS2564: Property Has No Initializer in TypeScript
- Fix "Property does not exist on Window" in TypeScript
- TypeScript Getter Setter Errors: TS1056, TS1028, TS2378 Fix
- JSX.Element vs ReactNode vs ReactElement: TS2322 Fix
- Fix TS7016: Could Not Find Declaration File for Module
- Fix TS2305: Module Has No Exported Member in TypeScript
Importing ReactiveFormsModule into the correct imports array — whether in an NgModule or a standalone component’s @Component decorator — is the definitive fix for the Can't bind to 'formGroup' error, and once it’s in place your reactive forms will compile and behave as expected.
Frequently Asked Questions
One email a month — no fluff
RLS gotchas, Next.js cache debugging, and the one Supabase setting that bit me last month.
Continue Reading
Fix "Property does not exist on Window" in TypeScript
Learn how to safely extend the Window interface in TypeScript using declaration merging, type assertions, and bracket notation to avoid compile-time errors.
Fix TS2564: Property Has No Initializer in TypeScript
TS2564 fires when a class field is typed but never guaranteed to be set. The fix depends on WHY it's unset: a default value, constructor assignment, the definite-assignment `!`, or an optional `?`. Picking the wrong one hides real bugs.
Fix Module not found: Can't resolve 'encoding' in Vercel
You see "Module not found: Can't resolve 'encoding'" in your Vercel deployment logs. This guide explains why it happens with cross-fetch/node-fetch and how to fix it permanently.
Browse by Topic
Find stories that matter to you.
