Question
Angular RouterLink Error: Import RouterModule in the Correct NgModule
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.
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:
- Angular compiles
HeaderComponent. - It sees
routerLinkon the<a>element. - Angular checks
LayoutModule, because that module declaresHeaderComponent. LayoutModuleimportsRouterModule, which exports theRouterLinkdirective.- Angular recognizes the directive and creates the link successfully.
- The user clicks Sign in.
RouterLinksends the/signinnavigation request to Angular's router.- The router matches the
signinroute registered byUsersRoutingModule. - Angular renders
SigninComponentin 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.
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
RouterModulein modules that only need directives such asrouterLink,routerLinkActive, orrouter-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
routerLinkActiveto 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
| Approach | Main purpose | Where to use it |
|---|---|---|
RouterModule.forRoot(routes) | Creates the root router configuration and providers | Once, usually in AppRoutingModule |
RouterModule.forChild(routes) | Registers routes owned by a feature module | Feature routing modules such as UsersRoutingModule |
RouterModule | Makes routing directives available in templates | Modules declaring components that use routerLink or router-outlet |
routerLink="/signin" | Navigate to a fixed internal URL declaratively | Standard template links |
[routerLink]="['/users', id]" | Build a route from dynamic segments | Detail pages and parameterized links |
Router.navigate(...) | Navigate imperatively from TypeScript | After saves, login, validation, or other code-driven events |
href="/signin" | Browser navigation | Usually external URLs; not preferred for Angular internal navigation |
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.
RouterModuleprovides template directives such asRouterLink.- A component outside a router outlet can still navigate with
routerLink. - Use
forRoot()once; useforChild()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.
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
Keep learning
Related questions
@Directive vs @Component in Angular: Differences, Use Cases, and When to Use Each
Learn the difference between @Directive and @Component in Angular, including use cases, examples, and when to choose each.
Accessing Input Value from EventTarget in TypeScript
Learn why EventTarget has no value property in TypeScript and safely read values from HTML input events in Angular applications.
Angular (change) vs (ngModelChange): What’s the Difference?
Learn the difference between Angular (change) and (ngModelChange), when each fires, and which one to use in forms and inputs.