Angular Complete Route Configuration: Every Route Property Explained [2026]

Link copied
Angular Complete Route Configuration: Every Route Property Explained [2026]

Angular Complete Route Configuration: Every Route Property Explained [2026]

Angular Tutorial Module 5: Routing Lesson 5.2

The Angular Route object has about twenty properties, but most tutorials only show the three you need for hello-world (path, component, redirectTo). The other seventeen are where real applications live — nested routes, named outlets, route-level providers, runtime data, and the matcher options that decide which route wins. This lesson is the reference page you come back to.

This is lesson 5.2, following lesson 5.1's router essentials. Treat this lesson as a lookup. You won't memorize every property; you'll grep this page when a Route doesn't behave the way you expect.

The full table #

Every property on the Route type, with what it does:

Property Type Purpose
path string URL segment to match
component Type<Component> Component to render
loadComponent () => Promise<...> Lazy-loaded component (lesson 5.6)
loadChildren () => Promise<Routes> Lazy-loaded sub-routes
children Routes Nested routes; render inside this route's component
redirectTo string | (...) => UrlTree Redirect to another URL or function-computed UrlTree
pathMatch 'prefix' | 'full' How path should match (default prefix)
outlet string Named outlet (default unnamed/primary)
matcher UrlMatcher Fully-custom URL matching function
data any Static data attached to the route, available as input or via ActivatedRoute
resolve Record<string, ResolveFn> Async data resolvers — runs before route activates (lesson 5.5)
canActivate CanActivateFn[] Functional guards — must return true for route to activate (lesson 5.3)
canActivateChild CanActivateChildFn[] Like canActivate but for any descendant route
canDeactivate CanDeactivateFn<T>[] Run when leaving the route — block navigation if returns false
canMatch CanMatchFn[] Run BEFORE matching — useful for feature flags
providers Provider[] DI providers scoped to this route + its children
title string | ResolveFn<string> Document title for this route
runGuardsAndResolvers enum When to re-run guards/resolvers on same-URL navigation

Twenty properties. Most routes use 2-4 of them. The rare ones earn their keep when you need them.

pathMatch: 'prefix' | 'full' #

The matcher defaults to prefix — a route matches if its path is a PREFIX of the URL. With full, the path must match the ENTIRE remaining URL.

The distinction matters most for empty-path redirects:

// ❌ Wrong — matches EVERYTHING (every URL starts with '')
{ path: '', redirectTo: '/home', pathMatch: 'prefix' }

// ✅ Right — matches only the empty path
{ path: '', redirectTo: '/home', pathMatch: 'full' }

When redirectTo is set, the Angular compiler will error if pathMatch: 'full' is missing on an empty-path route. For non-empty paths the prefix default is almost always what you want.

children — nested routes #

A route can have child routes that render inside ITS <router-outlet />:

export const routes: Routes = [
  {
    path: 'settings',
    component: SettingsShellPage,
    children: [
      { path: 'profile', component: ProfilePage },
      { path: 'security', component: SecurityPage },
      { path: '', redirectTo: 'profile', pathMatch: 'full' },
    ],
  },
];

In SettingsShellPage's template:

<nav>
  <a routerLink="profile">Profile</a>      <!-- relative — resolves to /settings/profile -->
  <a routerLink="security">Security</a>
</nav>
<router-outlet />

The shell renders consistently; the outlet swaps the child. The empty-path redirect ensures /settings auto-redirects to /settings/profile.

Nested routes scale to any depth — useful for admin areas, settings pages, multi-step wizards.

outlet — named (secondary) outlets #

A route can render into a NAMED outlet instead of the primary one:

<router-outlet />                           <!-- primary -->
<router-outlet name="sidebar" />            <!-- named -->
export const routes: Routes = [
  { path: '', component: HomePage },
  { path: 'help', component: HelpPanel, outlet: 'sidebar' },
];

Navigate the named outlet via URL syntax:

<a [routerLink]="[{ outlets: { sidebar: ['help'] } }]">Show help</a>

The URL becomes /(sidebar:help). Use for persistent panels — sidebars, modal slots, chat windows — that change independently of the main content.

Rare in everyday apps; useful for app shells with multiple independently-routed regions.

data — static route metadata #

Attach arbitrary data to a route:

{
  path: 'admin',
  component: AdminDashboard,
  data: { requiresRole: 'admin', showSidebar: false },
}

With withComponentInputBinding() (lesson 5.1), the component receives data as inputs:

export class AdminDashboard {
  requiresRole = input<string>();
  showSidebar = input<boolean>();
}

Used for breadcrumbs, role requirements consumed by guards, feature flags, page titles, anything route-tied that the component or a guard needs at runtime.

providers — route-scoped DI #

A route can register providers visible only to itself and its children:

{
  path: 'shop',
  component: ShopPage,
  providers: [
    CartService,                              // new instance per shop route
    { provide: API_BASE, useValue: '/api/shop' },
  ],
  children: [...],
}

The CartService is a fresh instance when the user enters /shop; it dies when they leave. Useful for feature-scoped state (a shopping cart that resets when the user navigates away) and for environment-token overrides per area of the app.

The alternative — providing at the root and managing lifecycle yourself — works but couples the service's lifetime to the app's. Route-scoped providers tie the lifetime to the user's presence on the route.

redirectTo with a function #

Classic string-form redirect:

{ path: 'old-path', redirectTo: '/new-path', pathMatch: 'full' }

v16+ adds function-form redirect for dynamic logic:

{
  path: 'old/:id',
  redirectTo: ({ params }) => `/new/${params['id']}`,
  pathMatch: 'full',
}

Return a string or a UrlTree. Useful for renaming routes while preserving the dynamic segments.

runGuardsAndResolvers — when to re-run #

Default: when the user navigates to the SAME URL, guards and resolvers don't re-run (Angular assumes nothing changed). Override:

{
  path: 'dashboard',
  component: DashboardPage,
  resolve: { data: dashboardResolver },
  runGuardsAndResolvers: 'always',
}

Options:

  • 'paramsChange' (default) — re-run only if path params change
  • 'paramsOrQueryParamsChange' — also if query params change
  • 'always' — every navigation, even same-URL
  • 'pathParamsChange' / 'pathParamsOrQueryParamsChange' — refined variants

Use 'always' for routes whose data is time-sensitive (a stale dashboard the user wants to refresh) and you want clicking the same nav link to re-fetch.

canMatch vs canActivate — when each runs #

Both gate route activation but at different times:

Guard When it runs Failure result
canMatch BEFORE Angular even considers this route — during URL matching Route is skipped; the matcher tries the next one
canActivate AFTER the route is matched, BEFORE the component renders Navigation is cancelled

Use canMatch for feature flags ("if the beta feature is off, this route doesn't exist") and for routes that should fall through to a wildcard. Use canActivate for auth ("this route exists, but you can't access it without being logged in — redirect to login").

Lesson 5.3 covers the full guard API.

A realistic route config #

export const routes: Routes = [
  // Public
  { path: '', component: HomePage, title: 'Welcome' },
  { path: 'login', component: LoginPage, title: 'Sign in', canActivate: [redirectIfLoggedIn] },

  // Authenticated area with nested admin
  {
    path: 'app',
    component: AppShell,
    canActivate: [authGuard],
    children: [
      { path: '', redirectTo: 'dashboard', pathMatch: 'full' },
      { path: 'dashboard', component: Dashboard, title: 'Dashboard', resolve: { data: dashboardResolver } },
      { path: 'users', component: UserListPage, title: 'Users' },
      { path: 'users/:id', component: UserDetailPage },
      {
        path: 'admin',
        component: AdminShell,
        canActivate: [adminGuard],
        providers: [AdminApiClient],
        children: [
          { path: 'roles', component: RolesPage },
          { path: 'settings', component: AdminSettingsPage },
        ],
      },
    ],
  },

  // Beta feature — only matches if flag is on
  { path: 'beta-feature', component: BetaPage, canMatch: [betaFlagMatcher] },

  // 404
  { path: '**', component: NotFoundPage, title: 'Not found' },
];

Real patterns at work:

  • pathMatch: 'full' on the empty-path redirect inside /app
  • children for nested routes — /app/dashboard, /app/users, etc.
  • canActivate: [authGuard] on the parent — auth required for the whole /app subtree
  • Stacked guards (canActivate: [authGuard] on /app, canActivate: [adminGuard] on /app/admin)
  • Route-scoped providers for admin-only services
  • canMatch for beta features that disappear from routing entirely when off
  • Wildcard ** for 404

Most real-world apps look like this — a few public routes, an authenticated shell with nested children, a 404 catch-all.

Common gotchas #

Symptom Cause Fix
Empty-path route doesn't redirect Missing pathMatch: 'full' Required for redirectTo on ''
Wildcard never matches ** is not LAST in the routes array Reorder — ** always last
Nested route 404s Missing <router-outlet /> in the parent component's template Add it
Same-URL navigation does nothing Default runGuardsAndResolvers: 'paramsChange' Set 'always' for time-sensitive routes
Route-scoped providers don't see updates Each navigation reuses the same instance unless you exit/re-enter the route Provider lifetime = route activation lifetime; refresh by exit/re-enter
Named outlet doesn't render Outlet missing name="...", or routerLink syntax wrong Both need to use the same outlet name

What's next #

Lesson 5.3 covers functional route guards in depth — CanActivateFn, CanDeactivateFn, CanMatchFn, ResolveFn. Lesson 5.4 catalogs router events (the 11 events fired during navigation). Lesson 5.5 covers data resolvers and prefetching. Lesson 5.6 closes Module 5 with lazy loading.

Try it yourself #

A mini app with nested routes, route data, and a route-scoped provider:

import { Component, inject, input } from '@angular/core';
import { provideRouter, withComponentInputBinding, RouterOutlet, RouterLink } from '@angular/router';
import { bootstrapApplication } from '@angular/platform-browser';
import { Injectable } from '@angular/core';

@Injectable() class FeatureService { name = 'I am route-scoped'; }

@Component({
  imports: [RouterOutlet, RouterLink],
  template: `
    <h2>Settings</h2>
    <nav><a routerLink="profile">Profile</a> | <a routerLink="security">Security</a></nav>
    <router-outlet />
    <p><small>Service says: {{ svc.name }}</small></p>
  `,
})
class SettingsShell { svc = inject(FeatureService); }

@Component({ template: `<p>Profile page (section: {{ section() }})</p>` })
class ProfilePage { section = input.required<string>(); }

@Component({ template: `<p>Security page</p>` })
class SecurityPage {}

@Component({
  selector: 'app-root',
  imports: [RouterOutlet, RouterLink],
  template: `<a routerLink="/settings">Settings</a> | <a routerLink="/">Home</a><router-outlet />`,
})
class App {}

bootstrapApplication(App, {
  providers: [provideRouter([
    { path: '', component: SettingsShell, redirectTo: 'settings', pathMatch: 'full' },
    {
      path: 'settings',
      component: SettingsShell,
      providers: [FeatureService],
      data: { section: 'settings' },
      children: [
        { path: '', redirectTo: 'profile', pathMatch: 'full' },
        { path: 'profile', component: ProfilePage },
        { path: 'security', component: SecurityPage },
      ],
    },
  ], withComponentInputBinding())],
});

Every property in this lesson — nested routes, empty-path redirect, route data, route-scoped providers — in one runnable example.

YouWhen should I use route-scoped providers vs providedIn: ‘root’?
YouRoute-scoped when the service’s state should DIE when the user leaves the area: a wizard’s draft data, a feature flag override, a per-feature API client with its own base URL. providedIn: 'root' when the service holds app-wide state (auth, theme, user prefs). The rule: if you’d want the state to reset on next navigation, scope it. If it should persist across routes, root it.

Lesson 5.3 picks up functional route guards in depth.

Up next in Angular

More from this topic

View all Angular articles →

Enjoyed this article?

Get new Angular tutorials delivered. No spam — just code-first articles when they ship.

Leave a Comment

Your email stays private. Required fields are marked *

Leave a Comment

Your email stays private. Required fields are marked *