Routes define the mapping between URL paths and the components that should be displayed. This guide covers all aspects of route configuration.
The Route Interface
Routes are defined using the Route interface from @angular/router:
export interface Route {
path ?: string ;
pathMatch ?: 'prefix' | 'full' ;
component ?: Type < any >;
loadComponent ?: () => Promise < Type < any >>;
redirectTo ?: string | RedirectFunction ;
outlet ?: string ;
canActivate ?: Array < CanActivateFn >;
canDeactivate ?: Array < CanDeactivateFn >;
canMatch ?: Array < CanMatchFn >;
children ?: Routes ;
loadChildren ?: LoadChildren ;
data ?: Data ;
resolve ?: ResolveData ;
title ?: string | ResolveFn < string >;
providers ?: Provider [];
// ... more properties
}
The complete Route interface is defined in packages/router/src/models.ts:567 with extensive documentation for each property.
Basic Route Configuration
Simple Routes
The most basic route maps a path to a component:
import { Routes } from '@angular/router' ;
import { HomeComponent } from './home/home.component' ;
import { AboutComponent } from './about/about.component' ;
import { ContactComponent } from './contact/contact.component' ;
export const routes : Routes = [
{ path: '' , component: HomeComponent },
{ path: 'about' , component: AboutComponent },
{ path: 'contact' , component: ContactComponent }
];
Route Parameters
Define dynamic route segments using the :paramName syntax:
import { UserDetailComponent } from './user-detail/user-detail.component' ;
import { ProductComponent } from './product/product.component' ;
export const routes : Routes = [
// Single parameter
{ path: 'user/:id' , component: UserDetailComponent },
// Multiple parameters
{ path: 'products/:category/:id' , component: ProductComponent },
// Optional segments with empty path children
{
path: 'search/:term' ,
component: SearchComponent
}
];
Accessing Route Parameters
Use ActivatedRoute to access route parameters:
import { Component , OnInit , inject } from '@angular/core' ;
import { ActivatedRoute } from '@angular/router' ;
import { map , switchMap } from 'rxjs/operators' ;
@ Component ({
selector: 'app-user-detail' ,
template: `
<h2>User Details</h2>
<p>User ID: {{ userId }}</p>
`
})
export class UserDetailComponent implements OnInit {
private route = inject ( ActivatedRoute );
userId : string = '' ;
ngOnInit () {
// Subscribe to parameter changes
this . route . params . subscribe ( params => {
this . userId = params [ 'id' ];
});
// Or use paramMap for type safety
this . route . paramMap . subscribe ( params => {
this . userId = params . get ( 'id' ) || '' ;
});
}
}
With component input binding enabled, route parameters automatically bind to component inputs:
import { provideRouter , withComponentInputBinding } from '@angular/router' ;
bootstrapApplication ( AppComponent , {
providers: [
provideRouter ( routes , withComponentInputBinding ())
]
});
import { Component , Input } from '@angular/core' ;
@ Component ({
selector: 'app-user-detail' ,
template: `<h2>User ID: {{ id }}</h2>`
})
export class UserDetailComponent {
@ Input () id !: string ; // Automatically populated from route param
@ Input () category ?: string ; // Query params also work
}
Wildcard Routes
Handle unknown routes with a wildcard route:
import { PageNotFoundComponent } from './page-not-found/page-not-found.component' ;
export const routes : Routes = [
{ path: 'home' , component: HomeComponent },
{ path: 'about' , component: AboutComponent },
// Wildcard route - must be last
{ path: '**' , component: PageNotFoundComponent }
];
Always place the wildcard route ** last in your route configuration. Routes are evaluated in order, and the wildcard matches everything.
Redirects
Static Redirects
Redirect from one path to another:
export const routes : Routes = [
// Redirect empty path to home
{ path: '' , redirectTo: '/home' , pathMatch: 'full' },
// Redirect old URL to new URL
{ path: 'old-about' , redirectTo: '/about' , pathMatch: 'full' },
// Preserve parameters in redirect
{ path: 'users/:id' , redirectTo: '/user/:id' , pathMatch: 'full' },
{ path: 'home' , component: HomeComponent },
{ path: 'about' , component: AboutComponent }
];
Path Matching Strategies
The pathMatch property determines how the router matches URLs:
'prefix' (default): Matches if the URL starts with the path
'full': Matches only if the URL exactly equals the path
export const routes : Routes = [
// Full match required - only matches exactly '/'
{ path: '' , redirectTo: '/home' , pathMatch: 'full' },
// Prefix match - matches '/team', '/team/11', '/team/11/user', etc.
{ path: 'team' , component: TeamComponent , pathMatch: 'prefix' }
];
Dynamic Redirects with RedirectCommand
Create conditional redirects using RedirectCommand:
import { inject } from '@angular/core' ;
import { Router , RedirectCommand } from '@angular/router' ;
import { AuthService } from './auth.service' ;
export const authGuard : CanActivateFn = ( route , state ) => {
const router = inject ( Router );
const authService = inject ( AuthService );
if ( ! authService . isLoggedIn ()) {
const loginUrl = router . parseUrl ( '/login' );
return new RedirectCommand ( loginUrl , {
skipLocationChange: true ,
state: { returnUrl: state . url }
});
}
return true ;
};
Child Routes
Create nested route hierarchies with child routes:
import { DashboardComponent } from './dashboard/dashboard.component' ;
import { OverviewComponent } from './dashboard/overview.component' ;
import { StatsComponent } from './dashboard/stats.component' ;
import { SettingsComponent } from './dashboard/settings.component' ;
export const routes : Routes = [
{
path: 'dashboard' ,
component: DashboardComponent ,
children: [
{ path: '' , redirectTo: 'overview' , pathMatch: 'full' },
{ path: 'overview' , component: OverviewComponent },
{ path: 'stats' , component: StatsComponent },
{ path: 'settings' , component: SettingsComponent }
]
}
];
The parent component needs a <router-outlet> for child routes:
import { Component } from '@angular/core' ;
import { RouterOutlet , RouterLink } from '@angular/router' ;
@ Component ({
selector: 'app-dashboard' ,
standalone: true ,
imports: [ RouterOutlet , RouterLink ],
template: `
<h1>Dashboard</h1>
<nav>
<a routerLink="overview">Overview</a>
<a routerLink="stats">Statistics</a>
<a routerLink="settings">Settings</a>
</nav>
<router-outlet></router-outlet> <!-- Child routes render here -->
`
})
export class DashboardComponent {}
Componentless Routes
Group routes without specifying a component:
export const routes : Routes = [
{
path: 'admin' ,
canActivate: [ adminGuard ], // Guard applies to all children
data: { role: 'admin' }, // Data shared with children
children: [
{ path: 'users' , component: AdminUsersComponent },
{ path: 'settings' , component: AdminSettingsComponent },
{ path: 'reports' , component: AdminReportsComponent }
]
}
];
Route Data and Title
Static Data
Pass static data to routes:
export const routes : Routes = [
{
path: 'profile' ,
component: ProfileComponent ,
data: {
title: 'User Profile' ,
animation: 'ProfilePage' ,
breadcrumb: 'Profile'
}
}
];
import { Component , OnInit , inject } from '@angular/core' ;
import { ActivatedRoute } from '@angular/router' ;
@ Component ({
selector: 'app-profile' ,
template: `<h1>{{ title }}</h1>`
})
export class ProfileComponent implements OnInit {
private route = inject ( ActivatedRoute );
title = '' ;
ngOnInit () {
this . route . data . subscribe ( data => {
this . title = data [ 'title' ];
});
}
}
Page Titles
Set page titles for better SEO and user experience:
export const routes : Routes = [
{
path: 'home' ,
component: HomeComponent ,
title: 'Home - My App'
},
{
path: 'about' ,
component: AboutComponent ,
title: 'About Us - My App'
},
{
path: 'user/:id' ,
component: UserComponent ,
title: userTitleResolver // Dynamic title
}
];
import { inject } from '@angular/core' ;
import { ResolveFn } from '@angular/router' ;
import { UserService } from './user.service' ;
export const userTitleResolver : ResolveFn < string > = ( route , state ) => {
const userService = inject ( UserService );
const userId = route . paramMap . get ( 'id' );
return `User ${ userId } - My App` ;
};
Custom URL Matchers
For complex URL patterns, use custom matchers:
import { UrlSegment , UrlMatchResult } from '@angular/router' ;
export function fileUrlMatcher ( segments : UrlSegment []) : UrlMatchResult | null {
// Match URLs ending with .html
if ( segments . length === 1 && segments [ 0 ]. path . endsWith ( '.html' )) {
return {
consumed: segments ,
posParams: {
filename: new UrlSegment ( segments [ 0 ]. path , {})
}
};
}
return null ;
}
export const routes : Routes = [
{
matcher: fileUrlMatcher ,
component: FileViewerComponent
}
];
URL matchers provide flexibility for routes that can’t be expressed with standard path syntax. See packages/router/src/models.ts:181 for the UrlMatcher type definition.
Route Providers
Provide services scoped to a route and its children:
import { provideHttpClient } from '@angular/common/http' ;
import { DataService } from './data.service' ;
export const routes : Routes = [
{
path: 'feature' ,
component: FeatureComponent ,
providers: [
DataService ,
provideHttpClient ()
],
children: [
// Children can inject DataService
]
}
];
Complete Example
Here’s a comprehensive route configuration:
import { Routes } from '@angular/router' ;
import { authGuard } from './guards/auth.guard' ;
import { adminGuard } from './guards/admin.guard' ;
export const routes : Routes = [
// Root redirect
{ path: '' , redirectTo: '/home' , pathMatch: 'full' },
// Simple routes
{ path: 'home' , component: HomeComponent , title: 'Home' },
{ path: 'about' , component: AboutComponent , title: 'About' },
// Route with parameters
{
path: 'user/:id' ,
component: UserDetailComponent ,
title: userTitleResolver
},
// Protected routes
{
path: 'dashboard' ,
component: DashboardComponent ,
canActivate: [ authGuard ],
children: [
{ path: '' , component: DashboardHomeComponent },
{ path: 'profile' , component: ProfileComponent },
{ path: 'settings' , component: SettingsComponent }
]
},
// Admin routes with guard
{
path: 'admin' ,
canActivate: [ adminGuard ],
data: { role: 'admin' },
children: [
{ path: 'users' , component: AdminUsersComponent },
{ path: 'reports' , component: AdminReportsComponent }
]
},
// Lazy loaded feature
{
path: 'products' ,
loadChildren : () => import ( './products/products.routes' )
. then ( m => m . PRODUCTS_ROUTES )
},
// Wildcard route
{ path: '**' , component: PageNotFoundComponent , title: '404 Not Found' }
];
Best Practices
Order Matters Define routes from most specific to least specific. The router uses first-match-wins strategy.
Use pathMatch: 'full' Always use pathMatch: 'full' with redirects from empty path to avoid infinite loops.
Type-Safe Parameters Use paramMap.get() instead of direct params access for better type safety.
Centralize Route Data Use the data property to store route metadata like titles, breadcrumbs, and permissions.
Next Steps
Route Guards Learn how to protect routes and control navigation with guards
Lazy Loading Optimize your app by lazy loading feature modules