Skip to main content
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:
app.routes.ts
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:
routes.ts
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:
user-detail.component.ts
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:
main.ts
import { provideRouter, withComponentInputBinding } from '@angular/router';

bootstrapApplication(AppComponent, {
  providers: [
    provideRouter(routes, withComponentInputBinding())
  ]
});
user-detail.component.ts
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:
routes.ts
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:
routes.ts
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:
auth.guard.ts
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:
routes.ts
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:
dashboard.component.ts
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:
routes.ts
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:
routes.ts
export const routes: Routes = [
  {
    path: 'profile',
    component: ProfileComponent,
    data: {
      title: 'User Profile',
      animation: 'ProfilePage',
      breadcrumb: 'Profile'
    }
  }
];
profile.component.ts
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:
routes.ts
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
  }
];
user-title.resolver.ts
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:
file-matcher.ts
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;
}
routes.ts
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:
routes.ts
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:
app.routes.ts
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

Build docs developers (and LLMs) love