angular/components · error

mat-form-field must contain a MatFormFieldControl.

Error message

mat-form-field must contain a MatFormFieldControl.

What it means

A <mat-form-field> requires exactly one form-field control (MatInput, MatSelect, MatChips, etc.) projected inside it. This error fires during ngAfterContentInit/ngAfterContentChecked when the field finds no projected MatFormFieldControl, meaning the field cannot wire label focus, hint, and error state.

Source

Thrown at src/material/form-field/form-field.ts:544

      this._processHints();
      this._changeDetectorRef.markForCheck();
    });

    // Update the aria-described by when the number of errors changes.
    this._errorChildren.changes.subscribe(() => {
      this._syncDescribedByIds();
      this._changeDetectorRef.markForCheck();
    });

    // Initial mat-hint validation and subscript describedByIds sync.
    this._validateHints();
    this._syncDescribedByIds();
  }

  /** Throws an error if the form field's control is missing. */
  private _assertFormFieldControl() {
    if (!this._control && (typeof ngDevMode === 'undefined' || ngDevMode)) {
      throw getMatFormFieldMissingControlError();
    }
  }

  private _updateFocusState() {
    const controlFocused = this._control.focused;

    // Usually the MDC foundation would call "activateFocus" and "deactivateFocus" whenever
    // certain DOM events are emitted. This is not possible in our implementation of the
    // form field because we support abstract form field controls which are not necessarily
    // of type input, nor do we have a reference to a native form field control element. Instead
    // we handle the focus by checking if the abstract form field control focused state changes.
    if (controlFocused && !this._isFocused) {
      this._isFocused = true;
      this._lineRipple?.activate();
    } else if (!controlFocused && (this._isFocused || this._isFocused === null)) {
      this._isFocused = false;
      this._lineRipple?.deactivate();
    }

View on GitHub (pinned to 0411926e7d)

Solutions

  1. Add the matInput directive to the input/textarea inside the form field and import MatInputModule
  2. Replace unsupported inner elements with a supported MatFormFieldControl (MatSelect, MatChipList, etc.) or drop mat-form-field
  3. Ensure the control is directly projected inside <mat-form-field> (not behind ngTemplateOutlet or unrelated wrappers without proper declaration)
  4. Verify the Material form-field/input modules are imported in the standalone component or NgModule

Example fix

// before
<mat-form-field>
  <input type="text" name="user">
</mat-form-field>
// after
<mat-form-field>
  <mat-label>User</mat-label>
  <input matInput type="text" name="user">
</mat-form-field>
Defensive patterns

Strategy: validation

Validate before calling

// template audit: every <mat-form-field> must contain an element with matInput
// or another MatFormFieldControl (mat-select, mat-chip-grid, etc.)
<mat-form-field>
  <input matInput [formControlName]="'name'">
</mat-form-field>

Prevention

When it happens

Trigger: Using mat-form-field with a plain <input>/<textarea> lacking matInput; wrapping unsupported elements (native select, button, div content); content projected incorrectly so the control query (ContentChild) is null.

Common situations: Forgetting the matInput directive on an input inside mat-form-field; using mat-form-field around a non-Material control; placing the input outside the form-field's content projection (e.g. after </mat-form-field>); typos like [matInput] without importing MatInputModule.

Related errors


AI-assisted analysis of angular/components@0411926e7d (2026-08-31). Data as JSON: /api/errors/3093885768dfe1b4. Report an issue: GitHub.