Asynchronous redirectTo function in Angular Routing

One of the new features in Angular 20.0.0-next.8 is the asynchronous redirect function. The redirect function redirects from a matched route path to another in the routing configuration. In version 19, the function's return type is string | UrlTree, which is a synchronous value. This feature allows the function to return an Observable or a Promise, which may be useful in some use cases. The Problem It Solves A static redirected route is sufficient in most cases. However, some scenarios may require the redirect function to make backend requests to obtain the routes based on dynamic criteria. The code changes allow the function to inject an HttpClient or use the fetch function to retrieve the value. The result is either an Observable or a Promise. Do not worry; the function continues to support string and UrlTree in v20. First, let's update Angular to the latest version 20. As of this writing, the Angular version is 20.0.0-next.9. ng update @angular/core @angular/cli --next In this demo, we will create some Pokemon routes that redirect to a general route to retrieve Pokemon details by name. Some redirected routes are Observables and some are Promises. For example, /pikachu will be redirected to Observable('/pokemon/pikachu') and /bulbasaur will be redirected to Promise.resolve('/pokemon/bulbasaur'). In real-world applications, they should be static strings, but they are returned as asynchronous values for demo purposes. Defining the App Configuration export const appConfig: ApplicationConfig = { providers: [ provideBrowserGlobalErrorListeners(), provideZonelessChangeDetection(), provideRouter(routes, withComponentInputBinding()), provideHttpClient(), ] }; The application provides HttpClient to make backend requests to retrieve Pokemon details and routes to redirect to a Pokemon component to display the details. Application Routes Here are the routes of the application. export const LINKS = [ { path: 'pikachu', label: 'Pikachu' }, { path: 'bulbasaur', label: 'Bulbasaur' }, ]; const pokemonRoutes: Routes = LINKS.map(({ path, label: title }, i) => { const route = `/pokemon/${path}`; return { path, title, redirectTo: () => i % 2 === 0 ? of(route) : Promise.resolve(route) } }); I made the pokemonRoutes to return an Observable for path pikachu and a Promise for path bulbasaur for demo reasons. export const routes: Routes = [ { path: 'home', loadComponent: () => import('./home/home.component'), }, ...pokemonRoutes, { path: 'pokemon/:id', resolve:{ pokemon: … preload Pokemon to make the component feel faster after navigation … }, loadComponent: () => import('./pokemon/pokemon.component'), }, … other routes… ]; The routes array included pikachu, bulbasaur and pokemon/:id. The pokemon/:id route preloads data so that the Pokemon details are available to the pokemon input of the Pokemon component. Display Route Links on Navigation Bar of App Component. I created a NavBarComponent to render the routerLink. {{ `${version} - ${name}` }} @for (link of links; track link.path) { {{ link.label }} } @Component({ selector: 'app-nav-bar', imports: [RouterLink], templateUrl: './nav-bar.component.html', styleUrl: './nav-bar.component.scss', changeDetection: ChangeDetectionStrategy.OnPush, }) export class NavBarComponent { version = VERSION.full; name = 'Asynchronous Redirect Demo'; links = LINKS; } When these links are clicked, they are redirected to /pokemon/:id and the pokemon component is loaded lazily to display the details. The behavior of redirect remains the same for synchronous and asynchronous values. @Component({ selector: 'app-root', imports: [RouterOutlet, NavBarComponent], templateUrl: './app.component.html', styleUrl: './app.component.scss', changeDetection: ChangeDetectionStrategy.OnPush, }) export class AppComponent {} In this code, the AppComponen serves as the root component of the Angular application. Its primary responsibility is to integrate and render the NavBarComponent. Conclusion When a path requires redirection, we can assign a function to the redirectTo property to redirect to the expected route. The function can return a string, an UrlTree, an Observable or a Promise. It is feasible to request an API to obtain data, construct the dynamic route, and return an Observable or Promise of the dynamic route. Resources The Async Redirect Function PR - https://github.com/angular/angular/pull/60863 The Redirect Function documentation: https://next.angular.dev/api/router/RedirectFunction Github Repo: https://github.com/railsstudent/angular20-new-features/blob/main/projects/async-redirect-demo/src/app/app.routes.ts Github Page: https://railss

May 5, 2025 - 06:02
 0
Asynchronous redirectTo function in Angular Routing

One of the new features in Angular 20.0.0-next.8 is the asynchronous redirect function. The redirect function redirects from a matched route path to another in the routing configuration. In version 19, the function's return type is string | UrlTree, which is a synchronous value. This feature allows the function to return an Observable or a Promise, which may be useful in some use cases.

The Problem It Solves

A static redirected route is sufficient in most cases. However, some scenarios may require the redirect function to make backend requests to obtain the routes based on dynamic criteria. The code changes allow the function to inject an HttpClient or use the fetch function to retrieve the value. The result is either an Observable or a Promise. Do not worry; the function continues to support string and UrlTree in v20.

First, let's update Angular to the latest version 20. As of this writing, the Angular version is 20.0.0-next.9.

ng update @angular/core @angular/cli --next

In this demo, we will create some Pokemon routes that redirect to a general route to retrieve Pokemon details by name. Some redirected routes are Observables and some are Promises. For example, /pikachu will be redirected to Observable('/pokemon/pikachu') and /bulbasaur will be redirected to Promise.resolve('/pokemon/bulbasaur'). In real-world applications, they should be static strings, but they are returned as asynchronous values for demo purposes.

Defining the App Configuration

export const appConfig: ApplicationConfig = {
 providers: [
   provideBrowserGlobalErrorListeners(),
   provideZonelessChangeDetection(),
   provideRouter(routes, withComponentInputBinding()),
   provideHttpClient(),
 ]
};

The application provides HttpClient to make backend requests to retrieve Pokemon details and routes to redirect to a Pokemon component to display the details.

Application Routes

Here are the routes of the application.

export const LINKS = [
   {
       path: 'pikachu',
       label: 'Pikachu'
   },
   {
       path: 'bulbasaur',
       label: 'Bulbasaur'
   },
];

const pokemonRoutes: Routes = LINKS.map(({ path, label: title }, i) => {
   const route = `/pokemon/${path}`;
   return {
       path,
       title,
       redirectTo: () => i % 2 === 0 ? of(route) : Promise.resolve(route)
   }
});

I made the pokemonRoutes to return an Observable for path pikachu and a Promise for path bulbasaur for demo reasons.

export const routes: Routes = [
   {
       path: 'home',
       loadComponent: () => import('./home/home.component'),
   },
   ...pokemonRoutes,
   {
       path: 'pokemon/:id',
       resolve:{
           pokemon:   preload Pokemon to make the component feel faster after navigation  
       },
       loadComponent: () => import('./pokemon/pokemon.component'),
   },
    other routes
];

The routes array included pikachu, bulbasaur and pokemon/:id. The pokemon/:id route preloads data so that the Pokemon details are available to the pokemon input of the Pokemon component.
Display Route Links on Navigation Bar of App Component.

I created a NavBarComponent to render the routerLink.

{{ `${version} - ${name}` }} class="links"> @for (link of links; track link.path) { [routerLink]="link.path">{{ link.label }} }

@Component({
 selector: 'app-nav-bar',
 imports: [RouterLink],
 templateUrl: './nav-bar.component.html',
 styleUrl: './nav-bar.component.scss',
 changeDetection: ChangeDetectionStrategy.OnPush,
})
export class NavBarComponent {
 version = VERSION.full;
 name = 'Asynchronous Redirect Demo';
 links = LINKS;
}

When these links are clicked, they are redirected to /pokemon/:id and the pokemon component is loaded lazily to display the details. The behavior of redirect remains the same for synchronous and asynchronous values.

 class="page-grid">
    class="navbar" />
    class="router-content">
        />