Question

Angular RouterLink Error: Import RouterModule in the Correct NgModule

typescriptangularangular2-routing

Question

In an Angular 2 application created with Angular CLI, the application shell renders a header above a <router-outlet>:

<!-- app.component.html -->
<app-header></app-header>
<router-outlet></router-outlet>
<app-footer></app-footer>

The header template contains a navigation link:

<!-- header.component.html -->
<a [routerLink]="['/signin']">Sign in</a>

Angular reports the following error:

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

AppRoutingModule imports and exports RouterModule, while HeaderComponent is declared in a separate LayoutModule. The signin route is configured in a feature routing module using RouterModule.forChild(). How can the header navigate to /signin, and why does Angular not recognize routerLink in this component?

Short Answer

You will learn that a component can use routerLink regardless of whether it is above or below a <router-outlet>. The error is caused by Angular's NgModule template scope: the NgModule that declares HeaderComponent must import RouterModule so that the RouterLink directive is available in the header template.

Concept

routerLink is not a built-in HTML property. It is an Angular directive supplied by RouterModule.

<a [routerLink]="['/signin']">Sign in</a>

When Angular compiles a component template, it checks the directives available in that component's template scope. In NgModule-based Angular applications, that scope comes from:

  • the NgModule that declares the component, and
  • the modules imported by that declaring NgModule.

In this case, HeaderComponent is declared by LayoutModule, not AppModule. Importing AppRoutingModule into AppModule makes routing directives available to templates declared in AppModule, but it does not automatically make them available to templates declared in LayoutModule.

The location of <app-header> relative to <router-outlet> is not the problem. A header, footer, sidebar, or any other component can contain router links. routerLink asks Angular's shared Router service to navigate; it does not require the component to be rendered inside the outlet.

Import RouterModule into LayoutModule to make RouterLink available to HeaderComponent. Importing RouterModule without calling forRoot() is safe for this purpose. RouterModule.forRoot(...) should be called only once, in the application's root routing configuration.

Mental Model

Think of an NgModule as a classroom and directives as tools kept in that classroom.

RouterModule brings tools such as routerLink and router-outlet. AppModule may have those tools, but HeaderComponent belongs to the separate LayoutModule classroom. Unless LayoutModule imports RouterModule, the header's template cannot use the routerLink tool.

The <router-outlet> is like a display area where the router places the selected page. A navigation button does not have to be inside that display area. It only needs access to the router tool.

Take Quiz

Syntax and Examples

Import RouterModule in every NgModule whose declared templates use routing directives.

// layout.module.ts
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { HeaderComponent } from './header/header.component';
import { FooterComponent } from './footer/footer.component';

@NgModule({
  declarations: [HeaderComponent, FooterComponent],
  imports: [RouterModule],
  exports: [HeaderComponent, FooterComponent]
})
export class LayoutModule {}

The header can then use routerLink normally:

<!-- header.component.html -->
<nav>
  <a routerLink="/signin">Sign in</a>
  <a [routerLink]="['/profile']">Profile</a>
</nav>

Both forms are valid:

  • routerLink="/signin" is convenient for a fixed URL.
  • [routerLink]="['/profile']" uses property binding and is useful when the link is built from route segments or component values.

For a link with a route parameter:

<a [routerLink]="['/users', userId]">View profile</a>

This produces a URL such as /users/42 when userId is 42.

Step by Step Execution

Consider this module structure:

// app.module.ts
@NgModule({
  imports: [
    BrowserModule,
    LayoutModule,
    UsersModule,
    AppRoutingModule
  ]
})
export class AppModule {}
// layout.module.ts
@NgModule({
  declarations: [HeaderComponent, FooterComponent],
  imports: [RouterModule],
  exports: [HeaderComponent, FooterComponent]
})
export class LayoutModule {}
<!-- header.component.html -->
<a routerLink="/signin">Sign in</a>

What happens:

  1. Angular compiles HeaderComponent.
  2. It sees routerLink on the <a> element.
  3. Angular checks LayoutModule, because that module declares HeaderComponent.
  4. LayoutModule imports RouterModule, which exports the RouterLink directive.
  5. Angular recognizes the directive and creates the link successfully.
  6. The user clicks Sign in.
  7. RouterLink sends the /signin navigation request to Angular's router.
  8. The router matches the signin route registered by UsersRoutingModule.
  9. Angular renders SigninComponent in the nearest <router-outlet>.

The header stays visible because it is part of the application shell, outside the routed content area.

Real World Use Cases

Routing links are commonly placed in shared layout components:

  • Top navigation: links to dashboard, sign-in, account settings, and help pages.
  • Sidebar menus: links to admin sections and nested application areas.
  • Footers: links to privacy policies, terms, and contact pages.
  • Breadcrumbs: links assembled from the current page hierarchy.
  • Data tables: links to detail pages such as /orders/1024.
  • Authentication UI: a sign-in link shown when no user session exists.

These components are often declared in a LayoutModule, SharedModule, or similar reusable module. Each such NgModule must import the directives its templates use.

Take Quiz

Real Codebase Usage

In NgModule-based applications, teams usually organize routing this way:

// app-routing.module.ts
const routes: Routes = [
  { path: '', redirectTo: 'signin', pathMatch: 'full' },
  { path: '**', component: PageNotFoundComponent }
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule {}
// users-routing.module.ts
const routes: Routes = [
  { path: 'signin', component: SigninComponent }
];

@NgModule({
  imports: [RouterModule.forChild(routes)],
  exports: [RouterModule]
})
export class UsersRoutingModule {}
// layout.module.ts
@NgModule({
  declarations: [HeaderComponent, FooterComponent],
  imports: [RouterModule],
  exports: [HeaderComponent, FooterComponent]
})
export class LayoutModule {}

Key patterns:

  • Call RouterModule.forRoot(routes) once to configure the application-wide router.
  • Use RouterModule.forChild(routes) in feature routing modules to add feature routes.
  • Import plain RouterModule in modules that only need directives such as routerLink, routerLinkActive, or router-outlet.
  • Put { path: '**', ... } last in the effective route order. It matches every unmatched URL, so placing it earlier prevents later routes from being reached.
  • Use routerLinkActive to style the current navigation item:
<a routerLink="/signin" routerLinkActive="active">Sign in</a>

Avoid calling router.navigate(...) directly from a template. Use routerLink for ordinary links; use Router.navigate() in TypeScript only when navigation depends on application logic, such as a successful form submission.

Common Mistakes

Importing routing only in AppModule

This does not expose routerLink to components declared by another NgModule:

// Broken when HeaderComponent is declared in LayoutModule
@NgModule({
  imports: [AppRoutingModule, LayoutModule]
})
export class AppModule {}

Fix: import RouterModule in LayoutModule.

@NgModule({
  imports: [RouterModule]
})
export class LayoutModule {}

Calling forRoot() in a shared or layout module

// Do not do this in LayoutModule
imports: [RouterModule.forRoot([])]

forRoot() configures router providers and should run once. A second call can cause provider and configuration problems. Use plain RouterModule instead.

Forgetting to export RouterModule from a routing module

A routing module that is intended to share router directives with its importing module should export RouterModule:

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule {}

Even with that export, another separate NgModule such as LayoutModule still needs its own import of RouterModule.

Putting the wildcard route first

const routes: Routes = [
  { path: '**', component: PageNotFoundComponent },
  { path: 'signin', component: SigninComponent }
];

The wildcard route catches /signin before Angular can reach the sign-in route. Put ** last.

Assuming the header must be inside <router-outlet>

This is unnecessary. The outlet controls where routed components render. Any component in the application can contain a router link when its template scope includes RouterLink.

Using a normal href for internal routes

<a href="/signin">Sign in</a>

This may trigger a full browser page load. Prefer Angular navigation for internal routes:

<a routerLink="/signin">Sign in</a>

Comparisons

ApproachMain purposeWhere to use it
RouterModule.forRoot(routes)Creates the root router configuration and providersOnce, usually in AppRoutingModule
RouterModule.forChild(routes)Registers routes owned by a feature moduleFeature routing modules such as UsersRoutingModule
RouterModuleMakes routing directives available in templatesModules declaring components that use routerLink or router-outlet
routerLink="/signin"Navigate to a fixed internal URL declarativelyStandard template links
[routerLink]="['/users', id]"Build a route from dynamic segmentsDetail pages and parameterized links
Router.navigate(...)Navigate imperatively from TypeScriptAfter saves, login, validation, or other code-driven events
href="/signin"Browser navigationUsually external URLs; not preferred for Angular internal navigation
Take Quiz

Cheat Sheet

// Root routing module: once per application
RouterModule.forRoot(routes)

// Feature routing module
RouterModule.forChild(featureRoutes)

// A module with components that use routerLink
imports: [RouterModule]
<!-- Fixed route -->
<a routerLink="/signin">Sign in</a>

<!-- Dynamic route segments -->
<a [routerLink]="['/users', userId]">User</a>

<!-- Active-link CSS class -->
<a routerLink="/signin" routerLinkActive="active">Sign in</a>

<!-- Routed component insertion point -->
<router-outlet></router-outlet>

Rules to remember:

  • Import dependencies where a component is declared, not merely where it is used.
  • RouterModule provides template directives such as RouterLink.
  • A component outside a router outlet can still navigate with routerLink.
  • Use forRoot() once; use forChild() for feature route definitions.
  • Put { path: '**' } after specific routes.

FAQ

Why does Angular say routerLink is not a known property?

Angular cannot find the RouterLink directive in the template scope of the component. Import RouterModule into the NgModule that declares that component.

Does a header need to be inside router-outlet to use routerLink?

No. A header can be above, below, or completely separate from the outlet. It only needs access to RouterModule in its declaring module.

Should LayoutModule import AppRoutingModule?

Usually no. LayoutModule only needs RouterModule to use routing directives. The root application module should own the routing configuration through AppRoutingModule.

What is the difference between forRoot() and forChild()?

forRoot() creates the main router configuration and providers once. forChild() adds routes from a feature module without creating another root router.

Why does /signin show the not-found page?

Check that the signin feature route is registered and that the wildcard ** route comes after routes that should match it.

Can I write routerLink="/signin" without square brackets?

Yes. Use the plain attribute for a static route. Use property binding, [routerLink], when creating the route from an array or component values.

When should I use Router.navigate() instead of routerLink?

Use routerLink for a clickable template link. Use Router.navigate() in TypeScript after logic such as saving a form, completing sign-in, or handling a button action.

Take Quiz

Mini Project

Description

Build a small application shell with a persistent header and footer. The header contains Angular router links, while the selected page appears in the central <router-outlet>. This demonstrates that shared layout components can navigate even when they are outside routed content.

Goal

Create working /signin and /help navigation links in a header declared by a separate LayoutModule.

Requirements

Requirement 1

Take Quiz

Keep learning

Related questions

View more