Skip to content

Fix: Angular Standalone Component Error — Component is Not a Known Element

FixDevs ·

Quick Answer

How to fix Angular standalone component errors — imports array, NgModule migration, RouterModule vs RouterLink, CommonModule replacement, and mixing standalone with module-based components.

The Problem

Angular throws a template error for a component that should be available:

Error: 'app-user-card' is not a known element:
1. If 'app-user-card' is an Angular component, then verify that it is included in the '@Component.imports' of this component.
2. If 'app-user-card' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '@Component.schemas' of this component to suppress this message.

Or using RouterLink in a standalone component breaks:

Error: Can't bind to 'routerLink' since it isn't a known property of 'a'.

Or a directive from Angular Material doesn’t work in a standalone component:

Error: 'mat-button' is not a known element
// or: Can't bind to 'matFormField' since it isn't a known property

Or migrating from NgModule to standalone components breaks existing functionality:

NullInjectorError: No provider for MatSnackBar!

Why This Happens

Angular’s standalone components (introduced in Angular 14, stable in Angular 15+) work differently from module-based components:

  • No NgModule injection — standalone components don’t belong to an NgModule. They can’t rely on NgModule imports to make other components/directives available in their template.
  • Explicit imports array required — every component, directive, or pipe used in a standalone component’s template must be listed in its imports array.
  • CommonModule broken into smaller pieces*ngIf, *ngFor, async, and other common directives from CommonModule are now available as individual standalone exports (NgIf, NgFor, AsyncPipe) or still via CommonModule.
  • Router directives need explicit importRouterLink, RouterOutlet, RouterLinkActive are now standalone directives and must be imported individually or via RouterModule.
  • Service providers may need to move — services provided in NgModules may not be available to standalone components unless they’re providedIn: 'root' or explicitly provided.

Fix 1: Add Components to the imports Array

Every non-HTML element in a standalone component’s template must be imported:

// WRONG — UserCardComponent used in template but not in imports
@Component({
  selector: 'app-user-list',
  standalone: true,
  imports: [],   // Empty — UserCardComponent not available
  template: `
    <app-user-card *ngFor="let user of users" [user]="user" />
  `,
})
export class UserListComponent {
  users = [];
}

// CORRECT — import everything used in the template
import { NgFor } from '@angular/common';
import { UserCardComponent } from './user-card.component';

@Component({
  selector: 'app-user-list',
  standalone: true,
  imports: [
    NgFor,              // For *ngFor
    UserCardComponent,  // For <app-user-card>
  ],
  template: `
    <app-user-card *ngFor="let user of users" [user]="user" />
  `,
})
export class UserListComponent {
  users = [];
}

Common Angular built-ins that need importing:

import {
  NgIf,
  NgFor,
  NgClass,
  NgStyle,
  NgSwitch, NgSwitchCase, NgSwitchDefault,
  AsyncPipe,
  DatePipe,
  DecimalPipe,
  CurrencyPipe,
  UpperCasePipe, LowerCasePipe,
  JsonPipe,
  SlicePipe,
} from '@angular/common';

// Or import all of them at once (larger bundle, but convenient during migration)
import { CommonModule } from '@angular/common';

New control flow syntax (Angular 17+) doesn’t need imports:

// Angular 17+ built-in control flow — no imports needed
@Component({
  standalone: true,
  imports: [UserCardComponent],  // Only UserCardComponent needed
  template: `
    @for (user of users; track user.id) {
      <app-user-card [user]="user" />
    }
    @if (isLoading) {
      <p>Loading...</p>
    }
  `,
})
export class UserListComponent {}

Fix 2: Fix Router Directives in Standalone Components

RouterLink, RouterOutlet, and RouterLinkActive must be explicitly imported:

// WRONG — RouterLink not imported
@Component({
  standalone: true,
  imports: [],
  template: `
    <a routerLink="/home">Home</a>
    <a routerLink="/about">About</a>
  `,
})
export class NavComponent {}

// CORRECT — import individual router directives
import { RouterLink, RouterLinkActive } from '@angular/router';

@Component({
  standalone: true,
  imports: [RouterLink, RouterLinkActive],
  template: `
    <a routerLink="/home" routerLinkActive="active">Home</a>
    <a routerLink="/about" routerLinkActive="active">About</a>
  `,
})
export class NavComponent {}

// Or import RouterModule for all router directives at once
import { RouterModule } from '@angular/router';

@Component({
  standalone: true,
  imports: [RouterModule],
  template: `
    <router-outlet />           <!-- RouterOutlet -->
    <a routerLink="/home">...</a>   <!-- RouterLink -->
  `,
})
export class AppShellComponent {}

Fix 3: Import Angular Material Components

Each Angular Material component must be imported individually in standalone components:

// WRONG — Material components not imported
@Component({
  standalone: true,
  imports: [],
  template: `
    <mat-card>
      <mat-form-field>
        <input matInput placeholder="Name" />
      </mat-form-field>
      <button mat-button>Submit</button>
    </mat-card>
  `,
})
export class ProfileFormComponent {}

// CORRECT — import each Material module used
import { MatCardModule } from '@angular/material/card';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';
import { MatButtonModule } from '@angular/material/button';

@Component({
  standalone: true,
  imports: [
    MatCardModule,
    MatFormFieldModule,
    MatInputModule,
    MatButtonModule,
  ],
  template: `
    <mat-card>
      <mat-form-field>
        <input matInput placeholder="Name" />
      </mat-form-field>
      <button mat-button>Submit</button>
    </mat-card>
  `,
})
export class ProfileFormComponent {}

Create a shared Material imports file to reduce repetition:

// material.imports.ts — shared Material imports
export const MATERIAL_IMPORTS = [
  MatCardModule,
  MatButtonModule,
  MatFormFieldModule,
  MatInputModule,
  MatTableModule,
  MatDialogModule,
  MatSnackBarModule,
  MatIconModule,
  // ... all Material modules your app uses
] as const;

// In any standalone component
@Component({
  standalone: true,
  imports: [...MATERIAL_IMPORTS, NgIf, RouterLink],
  template: `...`,
})
export class MyComponent {}

Fix 4: Fix Services Not Available in Standalone Components

Services that were provided via NgModule must be moved to root or explicitly provided:

// PROBLEM — service provided only in a NgModule
@NgModule({
  providers: [UserService],   // Only available to this module's components
})
export class UserModule {}

// Standalone component can't use UserService (NullInjectorError)
@Component({ standalone: true })
export class ProfileComponent {
  constructor(private userService: UserService) {}   // NullInjectorError
}

// FIX 1 — make service provided in root
@Injectable({ providedIn: 'root' })
export class UserService { ... }
// Now available everywhere — standalone and module-based

// FIX 2 — provide at the standalone component level
@Component({
  standalone: true,
  providers: [UserService],   // Available to this component and its children
})
export class ProfileComponent {
  constructor(private userService: UserService) {}
}

// FIX 3 — provide in bootstrapApplication (for root-level services)
// main.ts
bootstrapApplication(AppComponent, {
  providers: [
    UserService,
    MatSnackBar,          // For Material services
    provideRouter(routes),
    provideHttpClient(),
    provideAnimations(),
  ],
});

Fix 5: Bootstrap a Standalone Application

Setting up a fully standalone Angular application:

// main.ts — standalone bootstrap (no AppModule)
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app/app.component';
import { provideRouter } from '@angular/router';
import { provideHttpClient, withInterceptors } from '@angular/common/http';
import { provideAnimations } from '@angular/platform-browser/animations';
import { routes } from './app/app.routes';
import { authInterceptor } from './app/auth.interceptor';

bootstrapApplication(AppComponent, {
  providers: [
    provideRouter(routes),
    provideHttpClient(
      withInterceptors([authInterceptor]),
    ),
    provideAnimations(),
    // Feature-specific providers
    importProvidersFrom(MatSnackBarModule),
  ],
}).catch(console.error);

// app.routes.ts — lazy loading standalone components
export const routes: Routes = [
  {
    path: '',
    loadComponent: () =>
      import('./home/home.component').then(m => m.HomeComponent),
  },
  {
    path: 'users',
    loadComponent: () =>
      import('./users/user-list.component').then(m => m.UserListComponent),
  },
  {
    path: 'users/:id',
    loadComponent: () =>
      import('./users/user-detail.component').then(m => m.UserDetailComponent),
  },
];

Fix 6: Mix Standalone and NgModule Components

During migration, standalone and module-based components can coexist:

// Use a standalone component inside an NgModule
@NgModule({
  declarations: [OldComponent],
  imports: [
    StandaloneComponent,   // Import standalone component into NgModule
  ],
})
export class FeatureModule {}

// Use a module-based component inside a standalone component
@Component({
  standalone: true,
  imports: [
    NonStandaloneModule,   // Import entire NgModule into standalone component
    // All components/directives exported by NonStandaloneModule become available
  ],
  template: `<app-legacy-component />`,
})
export class NewStandaloneComponent {}

Mark a component as standalone:

// Before (module-based)
@Component({
  selector: 'app-user-card',
  template: `<div>{{ user.name }}</div>`,
})
export class UserCardComponent {}

// @NgModule declares it:
@NgModule({
  declarations: [UserCardComponent],
  exports: [UserCardComponent],
})
export class SharedModule {}

// After (standalone)
@Component({
  selector: 'app-user-card',
  standalone: true,   // Add this flag
  imports: [],        // Add needed imports
  template: `<div>{{ user.name }}</div>`,
})
export class UserCardComponent {}

// Remove from NgModule declarations — add to imports instead
@NgModule({
  imports: [UserCardComponent],    // From declarations to imports
  exports: [UserCardComponent],    // Keep if other modules need it
})
export class SharedModule {}

Fix 7: Run Angular’s Standalone Migration Schematic

Angular provides an automated migration tool:

# Migrate the entire project to standalone (Angular 15.2+)
ng generate @angular/core:standalone

# Options:
# --mode=convert-to-standalone  — convert components, directives, pipes
# --mode=prune-ng-modules       — remove unnecessary NgModules
# --mode=standalone-bootstrap   — switch to bootstrapApplication()

# Run each step separately for safety
ng generate @angular/core:standalone --mode=convert-to-standalone
# Review changes, test, then:
ng generate @angular/core:standalone --mode=prune-ng-modules
# Review changes, test, then:
ng generate @angular/core:standalone --mode=standalone-bootstrap

Still Not Working?

CUSTOM_ELEMENTS_SCHEMA — if you’re using actual Web Components (not Angular components) in your template, add CUSTOM_ELEMENTS_SCHEMA to suppress the “unknown element” error:

import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';

@Component({
  standalone: true,
  schemas: [CUSTOM_ELEMENTS_SCHEMA],   // Allow unknown HTML elements
  template: `<my-web-component></my-web-component>`,
})
export class MyComponent {}

Peer dependency issues with Angular Material — Angular Material 15+ has standalone support. Earlier versions require importProvidersFrom(MatModule) in providers. Verify your Material version matches Angular version.

provideRouter vs RouterModule.forRoot — in standalone bootstrap, use provideRouter(routes). In NgModule bootstrap, use RouterModule.forRoot(routes). Mixing these causes router to not initialize.

For related Angular issues, see Fix: Angular HTTP Interceptor Not Working and Fix: Angular Signals Not Updating.

F

FixDevs

Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.

Was this article helpful?

Related Articles