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 | RedirectFunction Redirect to another URL, or a function returning a string/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 on an empty-path route without pathMatch, the router throws a configuration error in dev mode as soon as the routes are registered. 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 instance is created the first time the user enters /shop and is shared by /shop and its children. By default the route's injector is NOT destroyed when the user leaves — re-entering /shop gets the same instance. If you want the state to reset on exit, opt in with provideRouter(routes, withAutoCleanupInjectors()) (v22.2; earlier v22 releases call it withExperimentalAutoCleanupInjectors()), which destroys injectors of inactive routes.

Route-scoped providers are great for feature isolation (the service is invisible outside /shop) and for environment-token overrides per area of the app — and, with auto-cleanup enabled, for state tied to the user's presence on the route.

redirectTo with a function #

Classic string-form redirect:

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

v18+ 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: guards and resolvers re-run only when path or matrix params change. 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). Note that navigating to the exact same URL is ignored by default — for clicking the same nav link to re-fetch, also set withRouterConfig({ onSameUrlNavigation: 'reload' }).

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 onSameUrlNavigation: 'ignore' plus runGuardsAndResolvers: 'paramsChange' withRouterConfig({ onSameUrlNavigation: 'reload' }) + runGuardsAndResolvers: 'always'
Route-scoped service keeps stale state after leaving Route injectors are not destroyed by default Add withAutoCleanupInjectors(), or reset the state explicitly
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 (every event 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: '', redirectTo: 'settings', pathMatch: 'full' },   // redirectTo and component can't be combined
    {
      path: 'settings',
      component: SettingsShell,
      providers: [FeatureService],
      children: [
        { path: '', redirectTo: 'profile', pathMatch: 'full' },
        { path: 'profile', component: ProfilePage, data: { section: 'settings' } },   // child routes don't inherit parent data by default
        { 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’?
Claude · used search_documentationRoute-scoped when the service belongs to one area of the app: a wizard’s draft data, a feature flag override, a per-feature API client with its own base URL. Note that route injectors survive navigation by default — add withAutoCleanupInjectors() if the state should die when the user leaves. providedIn: 'root' when the service holds app-wide state (auth, theme, user prefs). The rule: if only one area should see it, scope it (plus auto-cleanup if it must reset). If it should persist across the whole app, 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 *