In Angular, in what order does Router.events emit during a successful navigation, and which events can end a navigation?
answer
- start, recognise, guard, resolve, end
- RoutesRecognized after matching
- GuardsCheckStart and GuardsCheckEnd
- exactly one terminal event
basics
~10 sA successful Angular navigation emits NavigationStart, RoutesRecognized, GuardsCheckStart, GuardsCheckEnd, ResolveStart, ResolveEnd, then NavigationEnd. Every navigation ends with exactly one of NavigationEnd, NavigationCancel, NavigationError or NavigationSkipped.
solid answer
~30 sThe core sequence is `NavigationStart` (with the requested URL), `RoutesRecognized` (the URL has been matched, redirects applied), `GuardsCheckStart` and `GuardsCheckEnd` around the `canDeactivate`/`canActivateChild`/`canActivate` phase, `ResolveStart` and `ResolveEnd` around resolvers, and finally `NavigationEnd`, emitted after the new components have been activated. `RouteConfigLoadStart`/`RouteConfigLoadEnd` appear when a lazy route config loads, and the per-route `ActivationStart`/`ChildActivationStart` and `ActivationEnd`/`ChildActivationEnd` events appear inside the guard and activation phases. Each navigation finishes with exactly one terminal event: `NavigationEnd` on success, `NavigationCancel` when a guard, redirect or newer navigation stops it, `NavigationError` on an unexpected error, or `NavigationSkipped` when it was ignored, for example a same-URL navigation.
code
ts · 26 linesimport { Component, DestroyRef, inject } from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import {
GuardsCheckEnd,
NavigationCancel,
NavigationEnd,
NavigationError,
NavigationSkipped,
NavigationStart,
Router,
RouterOutlet,
} from '@angular/router';
@Component({ selector: 'app-root', imports: [RouterOutlet], template: '<router-outlet />' })
export class App {
constructor() {
inject(Router).events.pipe(takeUntilDestroyed(inject(DestroyRef))).subscribe((e) => {
if (e instanceof NavigationStart) console.log(e.id, 'start', e.url, e.navigationTrigger);
else if (e instanceof GuardsCheckEnd) console.log(e.id, 'guards passed?', e.shouldActivate);
else if (e instanceof NavigationEnd) console.log(e.id, 'end', e.urlAfterRedirects);
else if (e instanceof NavigationCancel) console.log(e.id, 'cancel code', e.code);
else if (e instanceof NavigationError) console.log(e.id, 'error', e.error);
else if (e instanceof NavigationSkipped) console.log(e.id, 'skipped code', e.code);
});
}
}go deeper
Recall the main milestones: start, routes recognised, guards, resolve, end, and that cancel and error are the other endings.
Explain the full order, which events carry which data, and that exactly one of four terminal events ends each navigation.
Use the sequence to diagnose stuck or bouncing navigations, recognising redirects as cancel-plus-new-start and superseded navigations by their codes.
Decide which navigation telemetry the app should record from these events, and keep per-event listeners from spreading business logic across the app.
## Where the events come from `Router.events` is a stream of objects describing each step of every navigation. They are classes exported from `@angular/router`, so you tell them apart with `instanceof` (or their `type` property). Each navigation has a numeric `id`, carried on the events that extend `RouterEvent`, so events from overlapping navigations can be told apart. ## The successful path, step by step 1. **`NavigationStart`** - a navigation has begun. It carries `url`, `navigationTrigger` (`'imperative'` for `navigate()`/`navigateByUrl()`/`routerLink`, `'popstate'` for back/forward, `'hashchange'`) and `restoredState` for history navigations. 2. **`RouteConfigLoadStart` / `RouteConfigLoadEnd`** - only when a `loadChildren` route configuration has to be downloaded during matching. 3. **`RoutesRecognized`** - matching is finished and redirects are applied. It carries `urlAfterRedirects` and the target `state` snapshot. `canMatch` guards have already run by now, as part of matching. 4. **`GuardsCheckStart`** - the guard phase begins. While it runs, **`ChildActivationStart`** and **`ActivationStart`** fire for routes being activated. 5. **`GuardsCheckEnd`** - all guards finished; its `shouldActivate` property says whether they allowed the navigation. 6. **`ResolveStart`** / **`ResolveEnd`** - the resolver phase (and blocking route resources). It is emitted even when no resolvers are configured, and skipped only when no route needs its checks rerun, such as a query-only change under the default rerun policy. 7. **`ActivationEnd`** / **`ChildActivationEnd`** - fired per route as the router activates the new components. 8. **`NavigationEnd`** - the navigation completed. It carries `url` and `urlAfterRedirects`; by now the new components exist. A lazily loaded `loadComponent` chunk downloads between `ResolveEnd` and activation, which also emits the `RouteConfigLoad` pair. ## The four ways a navigation ends | Terminal event | Meaning | Typical cause | |---|---|---| | `NavigationEnd` | success | everything passed | | `NavigationCancel` | stopped on purpose, with a `code` | guard returned `false`, a redirect, a newer navigation, a resolver with no value, `abort()` | | `NavigationError` | an unexpected error | no route matched, a guard or resolver threw, a chunk failed to load | | `NavigationSkipped` | ignored, never really started | same-URL navigation with `onSameUrlNavigation: 'ignore'` | The router's documentation states the rule interviewers look for: **every navigation ends with exactly one of these**. Code that waits only for `NavigationEnd` will wait forever after a cancelled or failed navigation. ## Details that change the picture - A **redirect is two navigations**. A guard returning a `UrlTree` or `RedirectCommand` produces `NavigationCancel` (code `Redirect`) for the first navigation and a new `NavigationStart` for the target; the first one never emits `GuardsCheckEnd`. - A guard returning **`false`** emits `GuardsCheckEnd` with `shouldActivate: false`, then `NavigationCancel` with code `GuardRejected`. - A **same-URL** navigation that is ignored emits `NavigationSkipped` **without** a preceding `NavigationStart`. - If a new navigation starts while one is running, the older one ends with `NavigationCancel` (code `SupersededByNewNavigation`). - `Scroll` events are also in the stream, emitted after navigations when scroll restoration is configured; they are not tied to one phase. ## Using the sequence in practice Knowing the order tells you where each concern belongs: - **Page-view analytics** go on `NavigationEnd`, using `urlAfterRedirects`, so redirected and cancelled navigations are not counted as views. - **Document titles** are applied by the router's title strategy right after it emits `NavigationEnd`, so a listener that reads `document.title` synchronously inside `NavigationEnd` still sees the old title. - **Focus management** for accessibility (moving focus to the new page heading) belongs after `NavigationEnd`, once the new components exist. - **Knowing the destination early**: `RoutesRecognized` is the first event with the matched target state, useful for starting work before guards finish. One typing trap: not every event extends `RouterEvent`. `RouteConfigLoadStart`/`End`, the four activation events and `Scroll` are separate classes without `id` and `url`, so filtering with `instanceof RouterEvent` silently drops them. Filter by the concrete classes you need instead. ## Seeing it for real `provideRouter(routes, withDebugTracing())` logs every router event to the console, which is the quickest way to learn the order, or to find where a stuck navigation stops. It is a debugging aid and should not ship to production.
- Why does a guard redirect produce two NavigationStart events?The router cannot turn the running navigation into a different one, so it ends the first navigation with `NavigationCancel` (code `Redirect`) and starts a new navigation to the redirect target, which has its own id and its own `NavigationStart`. The first navigation never reaches `GuardsCheckEnd`. Listeners that key on ids see two separate navigations.
- When in this sequence are the new route components created?During activation, after `ResolveEnd` (and after any lazy `loadComponent` chunk has loaded) and before `NavigationEnd`. `ActivationEnd` and `ChildActivationEnd` fire as each route is activated, so by the time a listener sees `NavigationEnd`, the routed components already exist.
saying these in an interview costs you the question
- Every navigation ends with NavigationEnd
- Resolvers run before GuardsCheckStart
- NavigationEnd fires before the new components are created
- A redirect keeps the same navigation id and just changes its URL
- NavigationSkipped is preceded by NavigationStart