angular/components · error

Missing definitions for header, footer, and row; cannot dete

Error message

Missing definitions for header, footer, and row; cannot determine which columns should be rendered.

What it means

The CDK table throws this when, after content initialization, it found no header row definitions (matHeaderRowDef/cdkHeaderRowDef), no footer row definitions, and no row definitions (matRowDef/cdkRowDef). Without at least one row definition the table cannot know which columns to render or how to project rows. It only runs in development mode (ngDevMode).

Source

Thrown at src/cdk/table/table.ts:1008

  /** Whether the table has all the information to start rendering. */
  private _canRender(): boolean {
    return this._hasAllOutlets && this._hasInitialized;
  }

  /** Renders the table if its state has changed. */
  private _render(): void {
    // Cache the row and column definitions gathered by ContentChildren and programmatic injection.
    this._cacheRowDefs();
    this._cacheColumnDefs();

    // Make sure that the user has at least added header, footer, or data row def.
    if (
      !this._headerRowDefs.length &&
      !this._footerRowDefs.length &&
      !this._rowDefs.length &&
      (typeof ngDevMode === 'undefined' || ngDevMode)
    ) {
      throw getTableMissingRowDefsError();
    }

    // Render updates if the list of columns have been changed for the header, row, or footer defs.
    const columnsChanged = this._renderUpdatedColumns();
    const rowDefsChanged = columnsChanged || this._headerRowDefChanged || this._footerRowDefChanged;
    // Ensure sticky column styles are reset if set to `true` elsewhere.
    this._stickyColumnStylesNeedReset = this._stickyColumnStylesNeedReset || rowDefsChanged;
    this._forceRecalculateCellWidths = rowDefsChanged;

    // If the header row definition has been changed, trigger a render to the header row.
    if (this._headerRowDefChanged) {
      this._forceRenderHeaderRows();
      this._headerRowDefChanged = false;
    }

    // If the footer row definition has been changed, trigger a render to the footer row.
    if (this._footerRowDefChanged) {
      this._forceRenderFooterRows();

View on GitHub (pinned to 0411926e7d)

Solutions

  1. Add a row definition inside the table: <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
  2. Add a header row definition: <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
  3. Ensure row/header/footer directives are direct projected children of the table so ContentChildren queries pick them up
  4. Verify the MatTableModule/CdkTableModule is imported so directives are recognized

Example fix

// before
<table mat-table [dataSource]=data>
  <ng-container matColumnDef="name"><th mat-header-cell *matHeaderCellDef>Name</th><td mat-cell *matCellDef="let e">{{e.name}}</td></ng-container>
</table>
// after
<table mat-table [dataSource]=data>
  <ng-container matColumnDef="name"><th mat-header-cell *matHeaderCellDef>Name</th><td mat-cell *matCellDef="let e">{{e.name}}</td></ng-container>
  <tr mat-header-row *matHeaderRowDef="['name']"></tr>
  <tr mat-row *matRowDef="let row; columns: ['name'];"></tr>
</table>
Defensive patterns

Strategy: validation

Validate before calling

const hasRowDefs =
  !!document.querySelector('tr[matRowDef], tr[mat-header-row], tr[matHeaderRowDef]') ||
  !!(templateRefs && templateRefs.some(t => /matRowDef|matHeaderRowDef/.test(t)));
if (!hasRowDefs) throw new Error('Table requires a *matRowDef and *matHeaderRowDef');

Type guard

function hasTableRows(tpl: string): boolean {
  return /matHeaderRowDef/.test(tpl) && /matRowDef/.test(tpl);
}

Try / catch

try {
  this.appRef.attachView(tableHostView);
} catch (e) {
  if (/Missing definitions for header, footer, and row/.test(e.message)) {
    console.error('Add <tr mat-row *matRowDef> and <tr mat-header-row *matHeaderRowDef> to the table.');
  }
}

Prevention

When it happens

Trigger: CdkTable.ngAfterContentInit lifecycle when this._headerRowDefs.length === 0 && this._footerRowDefs.length === 0 && this._rowDefs.length === 0. E.g. a <table mat-table> with columns declared but no <tr mat-row> or <tr mat-header-row> elements, or row defs placed outside the table content projection.

Common situations: Forgetting the <tr mat-row *matRowDef="let row; columns: displayedColumns;"> line after copy-pasting a table skeleton; defining rows in a component whose ContentChildren don't reach the table because of an intermediate component boundary; typos in directive selectors (e.g. matRowDefs).

Related errors


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