Programmatic Navigation
Declarative links with routerLink cover most navigation, but real applications frequently need to navigate from code: after a form submits, once a login succeeds, when a timer fires, or inside a guard. Angular’s Router service exposes two imperative methods for this — navigate and navigateByUrl — plus a rich set of navigation extras for query params, fragments, relative paths, and route state. This page shows how to use them correctly in modern standalone, signal-based Angular.
Injecting the Router
Navigation starts by injecting the Router service. In modern Angular you use the inject() function rather than constructor parameters, which keeps components terse and works inside field initializers.
import { Component, inject } from '@angular/core';
import { Router } from '@angular/router';
@Component({
selector: 'app-login',
standalone: true,
template: `<button (click)="signIn()">Sign in</button>`,
})
export class LoginComponent {
private readonly router = inject(Router);
async signIn(): Promise<void> {
// ...authenticate the user...
await this.router.navigate(['/dashboard']);
}
}
Both navigation methods return a Promise<boolean> that resolves to true when navigation succeeds, false if it was cancelled (for example by a guard), and rejects if an error occurs.
navigate vs navigateByUrl
The two methods differ in how you describe the destination. navigate accepts an array of command segments that Angular composes into a URL, while navigateByUrl takes a single, fully-formed absolute URL string.
| Aspect | navigate(commands, extras?) | navigateByUrl(url, extras?) |
|---|---|---|
| Input | Array of path segments | Absolute URL string |
| Relative navigation | Supported via relativeTo | Not supported (always absolute) |
| Param/segment building | Angular encodes each segment | You build the string yourself |
| Best for | Composing routes dynamically | Redirecting to a known full URL |
// Equivalent destinations
this.router.navigate(['/users', userId, 'profile']);
this.router.navigateByUrl(`/users/${userId}/profile`);
Prefer
navigatewith a commands array when any segment is dynamic. Angular URL-encodes each segment for you, so a value likejohn doebecomesjohn%20doeautomatically instead of producing a broken URL.
Passing navigation extras
Both methods accept a NavigationExtras object as a second argument. This is how you attach query parameters, fragments, and other behavior to the navigation.
this.router.navigate(['/search'], {
queryParams: { q: 'angular', page: 2 },
fragment: 'results',
queryParamsHandling: 'merge',
});
That produces /search?q=angular&page=2#results. The most common extras:
| Extra | Type | Purpose |
|---|---|---|
queryParams | Params | Adds ?key=value pairs |
fragment | string | Adds the #fragment |
queryParamsHandling | 'merge' | 'preserve' | '' | Keeps or merges existing query params |
relativeTo | ActivatedRoute | Anchor for relative navigation |
state | Record<string, unknown> | Transient data not shown in the URL |
replaceUrl | boolean | Replaces history instead of pushing |
skipLocationChange | boolean | Navigates without changing the URL bar |
Relative navigation
By default, command arrays are resolved against the application root (absolute). To navigate relative to the current route, pass an ActivatedRoute via relativeTo. This is invaluable inside nested routes where you should not hardcode the full path.
import { Component, inject } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';
@Component({
selector: 'app-user-detail',
standalone: true,
template: `<button (click)="editUser()">Edit</button>`,
})
export class UserDetailComponent {
private readonly router = inject(Router);
private readonly route = inject(ActivatedRoute);
editUser(): void {
// From /users/42 -> /users/42/edit
this.router.navigate(['edit'], { relativeTo: this.route });
}
goToSibling(): void {
// From /users/42 -> /users/99 using a parent-relative path
this.router.navigate(['../99'], { relativeTo: this.route });
}
}
The .. segment walks up one level just like a filesystem path, letting you reach sibling routes without knowing the absolute structure.
Passing transient state
Use state to hand data to the destination component without exposing it in the URL. It is ideal for things like a flash message or a pre-fetched object. The receiving component reads it from the current Navigation.
// Sender
this.router.navigate(['/orders', orderId], {
state: { justCreated: true },
});
// Receiver
import { Component, inject } from '@angular/core';
import { Router } from '@angular/router';
@Component({
selector: 'app-order',
standalone: true,
template: `@if (justCreated) { <p class="banner">Order placed!</p> }`,
})
export class OrderComponent {
private readonly router = inject(Router);
protected readonly justCreated =
this.router.getCurrentNavigation()?.extras.state?.['justCreated'] ?? false;
}
statesurvives a page reload only if the browser restores it via the History API — treat it as best-effort UI data, never as a source of truth your component depends on.
Handling the navigation result
Because navigation is asynchronous and can be blocked by guards, inspect the resolved value when the outcome matters.
async checkout(): Promise<void> {
const ok = await this.router.navigate(['/checkout']);
if (!ok) {
console.warn('Navigation to /checkout was blocked by a guard');
}
}
Output:
Navigation to /checkout was blocked by a guard
Best practices
- Reach for
navigatewith a commands array as the default; encode-by-hand strings only when the full URL is already known. - Always pass
relativeTo: this.routefor navigation inside nested or lazy-loaded feature routes to avoid brittle absolute paths. - Use
queryParamsHandling: 'merge'when updating one filter so you don’t wipe out the user’s other query parameters. - Use
replaceUrl: trueafter redirects (such as login) so the back button doesn’t return to a now-invalid page. - Treat
stateas transient UI data, not as a reliable data channel — fetch authoritative data via a resolver instead. - Await the returned
Promise<boolean>whenever subsequent logic depends on the navigation actually completing.