Home/Blog/Programming/Angular routing guide: How to optimize app navigation
Home/Blog/Programming/Angular routing guide: How to optimize app navigation

Angular routing guide: How to optimize app navigation

16 min read
May 30, 2025
content
What is Angular Router?
Routing module and RouterOutlet
Path matching strategies in Angular routing
Prefix matching (Default)
Full path matching
What are wildcard routes?
What are child routes?
What are Angular route guards?
The routerLink directive
Example of routerLink
Adding active link styles
Lazy loading for optimized performance
Step 1: Setting up lazy loading
Step 2: Updating app routing module for lazy loading
Comparison: Lazy loading vs. eager loading
Preloading strategies in Angular: Optimizing module loading
Angular route resolver: Pre-fetching data for navigation
Best practices for optimizing Angular routing
What to learn next
Continue reading about Angular 2+ and front-end development

Become a Software Engineer in Months, Not Years

From your first line of code, to your first day on the job — Educative has you covered. Join 2M+ developers learning in-demand programming skills.

Key takeaways:

  • Angular’s router allows smooth navigation between components and pages. It also supports wildcard and child routes.

  • Lazy loading in Angular improves performance by loading only necessary modules when required.

  • Angular route guards, such as CanActivate and CanDeactivate, help control user access and navigation behavior.

  • Preloading strategies like PreloadAllModules enhance navigation speed without affecting lazy loading benefits.

  • Angular route resolvers fetch important data before rendering components to ensure a smooth user experience.

Imagine you’re building a large-scale Angular application with multiple views—without proper routing, your app could become a navigation nightmare. Users have trouble finding pages, bookmarking doesn’t work, and the back and forward buttons behave unpredictably.

Angular Router solves these challenges by providing a seamless, scalable navigation system.

Learning Angular

Cover
Learning Angular

In this course, you will learn Angular, which is currently among the top JavaScript frameworks. More developers are now seeking the best way to get started with this flexible and secure framework. You will learn how to achieve cross-platform high performance with the latest web techniques. You will begin with the basics of an Angular application, including its project structure. Then, you will learn about the basics of TypeScript and its importance in type checking. Next, you will cover how to create components and use pipes and directives in Angular. You’ll progress to cover event handling, navigation, and routing. You will end this course by learning how to create unit tests and debug an Angular application to prepare it for production. After completing this course, you will gain essential skills to develop apps by harnessing the power of the Angular command-line interface (CLI), write unit tests, style your apps by following the Material Design guidelines, and finally, deploy them to a hosting provider.

70hrs
Intermediate
137 Playgrounds
14 Quizzes

What is Angular Router?#

The Angular Router is a built-in module in Angular that enables Single-page applicationsA single-page application (SPA) is a web application that dynamically updates content on a single page without requiring full page reloads. It improves performance and user experience by fetching and rendering only the necessary data using JavaScript frameworks like Angular, React, or Vue. to navigate between views without reloading the page. It listens to the browser’s URL and dynamically loads components based on defined routes.

URLs consist of a domain name and a route definition, known as a path.

A path is a JavaScript object that the server uses to access a specific resource in the database. When the server serves our application, Angular will grab the path from the URL and match it against any valid paths we’ve set up.

Essentially, we set a key/value relationship with a path like /blog as the key and the desired page as the value.

This lets users move around your app easily and go straight to the page they want, without starting from the home component. Routing enables support for common browser behaviors like forward/back arrows and page bookmarking.

Router also contains tools for advanced behaviors like multiple router outlets, different path matching strategies, easy access to route parameters, and route guards to protect components from unauthorized access.

Routing module and RouterOutlet#

Routing modules are special parts of an Angular app that set up new routes and help control how users move between pages. All routing modules have the suffix -routing after their name, which is automatically added by Angular CLI.

Each routing module controls how pages change for the part of the app it is paired with — both parts usually have the same name. For example, the routing behavior for our home module would be in the routing module home-routing.

Here’s an example of a routing module for our home module, called home-routing.module.ts:

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { HomeMainComponent } from './home-main/home-main.component';
 
const routes: Routes = [
  { path: '', component: HomeMainComponent }
];
 
@NgModule({
  imports: [RouterModule.forChild(routes)],
  exports: [RouterModule]
})
export class HomeRoutingModule { }

You can find our routes in the routes array variable. Each element of the routes array represents the route to a single component view.

The elements consist of two parts, the path property that provides a URL path and the component property that defines which component will be loaded at the provided path.

In this case, we use an empty string (which means a forward slash) to show that this is the homepage. It will load when someone just types the website’s main address. We then enter the name of the component Angular should fetch as our homepage, HomeMainComponent.

We can also use redirectto to direct empty path URLs to another page if we’d prefer users land elsewhere.

Next, we’ll need to remove HomeMainComponent from the exports of HomeModule. Because we’re using routing, we don’t need to export the component anymore. The Router will load the right component when a user visits the matching page.

Finally, we’ll replace the contents of the app.component.html file with the line:

<router-outlet></router-outlet>

Here, <router-outlet> acts as a placeholder for the component. Instead of setting a specific component, our template will show whatever component matches the URL that was typed in. By using this placeholder, we don’t have to export the component. Instead, we can export the module.

You can now view this app by entering http://localhost:4200 in your browser address bar.

Export relationships among modules
Export relationships among modules

To review, the HomeRoutingModule is a routing module where we define routes. We have one route that consists of a blank path. We’ll check if the client’s URL entry matches that path. If they are, we’ll load the homepage via HomeMainComponent.

The home page component is available due to a series of imports. First, we import the home-routing module into its paired standard module home. Then, we import the home module into the app module. Finally, we use the <router-outlet> directive in the app.component.html file to load the HomeMainComponent registered in the original routes array.

Path matching strategies in Angular routing#

In Angular routing, there are two main path matching strategies that dictate how URLs are matched against routes: prefix and full. Understanding these strategies is essential when defining routes that share common base paths or nested routes.

Prefix matching (Default)#

By default, Angular uses prefix matching, meaning the router will match a route if the beginning of the URL matches the defined path. For example:

const routes: Routes = [
  { path: 'products', component: ProductsComponent, pathMatch: 'prefix' }, // Default matching strategy
  { path: 'products/details', component: ProductDetailsComponent }
];

Here, both /products and /products/details will match the first route, because /products is a prefix of /products/details. This might lead to undesired behavior if you have more specific routes that should take precedence.

To avoid such issues, you can use the full path matching strategy.

Full path matching#

When using full path matching in Angular, the app will only go to a route if the entire URL matches exactly. This is helpful when you want a page to show only when the full link is typed, not just part of it.

To enable full matching, set pathMatch: 'full' in the route configuration:

const routes: Routes = [
  { path: '', redirectTo: '/home', pathMatch: 'full' },  // Full match for the root path
  { path: 'products', component: ProductsComponent, pathMatch: 'full' }
];

In this case, /products will only match ProductsComponent if the URL exactly matches /products. Any further nesting, like /products/details, will not match unless explicitly defined.

What are wildcard routes?#

If a user types in a path that doesn’t exist, we can stop an error by using something called a Wildcard Route. This special route catches any path that isn’t already listed and sends the user to a specific page. You can think of it like an “other” option that covers anything not already matched.

Most sites have a Wildcard that directs to a “404 Page Not Found” page. To create an error component for our app, enter the following into your command prompt:

ng generate component PageNotFound

We do not need a module because this error screen will just be simple text.

You can set a Wildcard by entering ** in place of a standard path in the routes array.

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { PageNotFoundComponent } from './page-not-found/page-not-found.component';
 
const routes: Routes = [
  { path: '**', component: PageNotFoundComponent }
];
 
@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

Now, any invalid URL will redirect to our 404 error page.

We need to make sure this component is imported last in the app.module.ts file for our other components to load properly. This is because Angular loads the component from the first matching path. If AppRoutingModule is imported first, Angular would always load PageNotFoundComponent because the Wildcard would always read as a match and therefore Angular would return that component.

  imports: [
    BrowserModule,
    HomeModule,
    AboutModule,
    ContactModule,
    AppRoutingModule,
  ],

The Wildcard at the bottom of the imports array ensures that any valid matches are returned and the 404 is only returned if there are no other matches.

What are child routes?#

Sometimes it makes sense to have routes categorized as a subgroup within a route. For example, our “About Us” page could feature separate subpages for info on the employees, /about/team and info on past clients, /about/clients. Child components are only rendered if the user is on the parent /about path.

First, we’ll generate the components by entering the following into our command prompt:

ng generate component about/team
ng generate component about/clients:

We then set these as children of the “About Us” page by adding a children array property to the about route in about-routing.module.ts.

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { AboutMainComponent } from './about-main/about-main.component'
import { BioComponent } from './bio/bio.component';
import { TeamComponent } from './team/team.component';
import { ClientsComponent } from './clients/clients.component';
 
const routes: Routes = [
  {
    path: '',
    component: AboutMainComponent,
    children: [
      { path: '', component: BioComponent },
      { path: 'team', component: TeamComponent },
      { path: 'clients', component: ClientsComponent },
    ]
  }
];
 
@NgModule({
  imports: [RouterModule.forChild(routes)],
  exports: [RouterModule]
})
export class AboutRoutingModule { }

The children array acts like a smaller version of the routes array, with similarly-formatted path and component properties. The difference is that child routes’ path properties are appended onto their parent path, meaning you do not need to write the full path.

For example, the full path to reach TeamComponent would be /about/team rather than just '/team'.

Finally, we’ll update the about-main.component.html template file with <router-outlet> to let it show any of the child components of about.

<h1>About Page</h1>
 
<router-outlet></router-outlet>

To get really good at Angular, you need to understand its main ideas and smart ways to build fast, strong apps. Start learning with a hands-on course that focuses more on practice and less on long video lessons. By the end, you’ll be equipped to create fully-fledged Angular apps with confidence.

Cover
Angular: Designing and Architecting Web Applications

This course will teach you step-by-step how to design and architect large Angular applications. You’ll start with the basics: components, pipes, and directives. These are the building blocks of any Angular application, so you will get plenty of hands-on practice with this material. Next, you’ll ramp things up by stepping into modules, routing, and RXJS, where you’ll learn how to declaratively manage data and dive deep into observables. In the latter half of the course, you’ll cover reactive and template forms (crucial for handling user data), as well as custom validators and setting up authentication with Firebase. By the end of this course, you'll be able to design large, scalable applications, be able to develop rich interactive components, and you’ll really understand how Angular works under the hood.

20hrs
Beginner
132 Playgrounds
4 Quizzes

What are Angular route guards?#

Route guards in Angular are important for keeping certain pages safe in our app. For example, we might want to stop users from accessing certain pages unless they’re logged in. Angular has a few built-in guards to help with this:

  • canActivate: Prevents navigation to a route if certain conditions aren’t met.

  • canDeactivate: Prevents leaving a route if certain conditions are not satisfied (e.g., unsaved form changes).

  • canLoad: Restricts lazy-loaded routes based on custom logic.

Here’s an example of using canActivate to protect a route:

const routes: Routes = [
  { path: 'admin', component: AdminComponent, canActivate: [AuthGuard] }
];

In this case, AuthGuard is a service that implements the canActivate interface and determines if the user is allowed to navigate to the AdminComponent.

Route guards enhance user experience and security, making sure users only access the routes meant for them.

Most Angular apps allow users to navigate through both direct URL entries and by clicking on links. While you can use standard <a> tags with the href attribute, this can result in page reloads, which isn’t ideal for a single-page application (SPA). Angular’s routerLink directive allows for seamless navigation without page reloads by utilizing the browser’s History API, making it faster and more efficient.

You might have noticed that our navigation bar example uses Bootstrap classes like .navbar and .nav-item for styling. Although Bootstrap isn’t required for Angular routing, it’s a popular CSS framework that helps create responsive and stylish navigation bars. You can use other CSS libraries or your own styles, but for this example, we’ll use Bootstrap.

Below is an example of a simple navigation menu using routerLink for SPA-friendly links, replace the contents of app.component.html with:

<nav class="navbar navbar-expand-md navbar-light bg-light mb-4">
  <a class="navbar-brand" routerLink="/">Website</a>
 
  <div class="collapse navbar-collapse">
    <ul class="navbar-nav mr-auto">
      <li class="nav-item">
        <a class="nav-link" routerLink="/about">About</a>
      </li>
      <li class="nav-item">
        <a class="nav-link" routerLink="/contact">Contact</a>
      </li>
    </ul>
  </div>
</nav>
 
<main class="container">
  <div class="card">
    <div class="card-body">
      <router-outlet></router-outlet>
    </div>
  </div>
</main>

The above code defines a basic navigation menu where the links (<a>) use routerLink instead of href. This allows users to navigate between pages without triggering a full page reload. The router-outlet directive is used to load the target component based on the current route.

It’s important to let users know which page they are currently on, and Angular makes this easy with the routerLinkActive directive. By using this directive on a navigation link, we can automatically add a CSS class when the route is active. This helps users see which page they’re currently on.

Instead of applying the active class manually, we can achieve this seamlessly by updating the <li> elements in our navigation bar to include routerLinkActive. This prevents duplication and keeps the logic for active links closer to the navigation setup.

<nav class="navbar navbar-expand-md navbar-light bg-light mb-4">
  <a class="navbar-brand" routerLink="/">Website</a>
 
  <div class="collapse navbar-collapse">
    <ul class="navbar-nav mr-auto">
      <li class="nav-item" routerLinkActive="active">
        <a class="nav-link" routerLink="/about">About</a>
      </li>
      <li class="nav-item" routerLinkActive="active">
        <a class="nav-link" routerLink="/contact">Contact</a>
      </li>
    </ul>
  </div>
</nav>
 
<main class="container">
  <div class="card">
    <div class="card-body">
      <router-outlet></router-outlet>
    </div>
  </div>
</main>

We’re applying the directive on the <li> elements with the nav-item class. This directive will check if the URL in the address bar matches the path in the routerLink directive.

If the path matches, we’ll add it to the active class to change the link text to show that it’s active with the darker text color.

Lazy loading for optimized performance#

Switching from eager loading to lazy loading improves performance.

Eager loading is when the browser is directed to load all components within the app module, regardless of which it will use.

Eager Loading at startup
Eager Loading at startup

Lazy loading splits the app into separate files so it only loads the parts it needs for the current page. This is usually preferred because it helps the page load faster by only loading the minimum data needed for each page.

Lazy Loading at startup
Lazy Loading at startup

Step 1: Setting up lazy loading#

To implement lazy loading, we first remove all module imports from app.module.ts:

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
 
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { PageNotFoundComponent } from './page-not-found/page-not-found.component';
 
@NgModule({
  declarations: [
    AppComponent,
    PageNotFoundComponent,
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

We still eager load PageNotFoundComponent, as it is low weight, and we could need it at any point.

Step 2: Updating app routing module for lazy loading#

We also need to update this information in the routes array found in app-routing.module.ts. This is the only routing module that will be sent at the user’s initial request. Angular can then use this file to load any future modules as needed.

const routes: Routes = [
  { path: '', loadChildren: () => import('./home/home.module').then(m => m.HomeModule) },
  { path: 'about', loadChildren: () => import('./about/about.module').then(m => m.AboutModule) },
  { path: 'contact', loadChildren: () => import('./contact/contact.module').then(m => m.ContactModule) },
  { path: '**', component: PageNotFoundComponent },
];

Notice that we’re not using the component property to tell Angular what component to load when the route is visited. Instead, we’re using the loadChildren property. This will tell Angular to lazy load a module. We’re setting it to an arrow function, which will request the module through the import() function. The import() function returns a promise. We chain the then() function to handle the response.

These loadChildren paths will pass along any previous path elements as a prefix for later paths. We must therefore update each of our routing modules’ Routes array to empty paths to ensure we do not repeat path names like /about/about.

{ path: '', component: AboutMainComponent }

Comparison: Lazy loading vs. eager loading#

Understanding the differences between lazy loading and eager loading helps in optimizing application performance and resource utilization.

Feature

Lazy Loading

Eager Loading

When to Load

Only when the route is accessed

During initial app load

Performance

Faster initial load, slower first route load

Slower initial load, faster subsequent navigation

Use Case

Large apps with many routes

Small apps with minimal routes

Preloading strategies in Angular: Optimizing module loading#

Lazy loading improves performance by only loading the parts of the app needed for the current view. But Angular also offers preloading strategies to balance lazy and eager loading. 

With preloading, certain parts of the app are loaded in the background after the app first loads, so future page loads are faster.

Angular provides a built-in strategy called PreloadAllModules:

@NgModule({
  imports: [
    RouterModule.forRoot(routes, { preloadingStrategy: PreloadAllModules })
  ]
})
export class AppRoutingModule { }

With PreloadAllModules, Angular will lazy-load modules after the initial page load, improving performance for subsequent navigations.

You can also create custom preloading strategies to have more control over which modules get preloaded based on specific conditions, making your application more efficient.

Angular route resolver: Pre-fetching data for navigation#

Resolvers in Angular ensure that the necessary data is fetched before a route is activated. This approach guarantees that components have the data they need as soon as they are rendered, improving user experience and preventing loading screens.

To create a resolver, implement the Resolve interface and use the resolve() method to fetch the required data:

@Injectable({ providedIn: 'root' })
export class UserResolver implements Resolve<User> {
  constructor(private userService: UserService) { }


  resolve(route: ActivatedRouteSnapshot): Observable<User> {
    return this.userService.getUser(route.paramMap.get('id'));
  }
}

You can now add this resolver to a route configuration:

const routes: Routes = [
  { path: 'user/:id', component: UserComponent, resolve: { user: UserResolver } }
];

This way, UserComponent will only activate once the user data is fetched, providing a seamless user experience.

Best practices for optimizing Angular routing#

  • Use lazy loading: Only load what’s necessary on demand to reduce initial load times.

  • Preload critical data: Use route resolvers for fetching essential data before route activation.

  • Minimize guard overhead: Ensure guards are lightweight and optimized.

  • Use named outlets: Named outlets allow you to render multiple views in a single route, useful for complex layouts.

What to learn next#

Congratulations on making a fully navigable Angular application! Routing is the key to keep users engaging with your app, especially for large applications.

However, it’s just one part of making an excellent Angular app.

Here are some more advanced concepts you’re ready to tackle along your Angular journey:

  • Advanced Routing (private routes, pairing CSS stylesheets)
  • Lifecycle Hooks
  • Modal Components
  • Authentication
  • Dependencies

To help you study these topics, Educative has created a course that teaches you how to create large-scale Angular applications using advanced techniques in a logical and efficient way.

You’ll even build a full-fledged application alongside the course.

Cover
Angular: Designing and Architecting Web Applications

This course will teach you step-by-step how to design and architect large Angular applications. You’ll start with the basics: components, pipes, and directives. These are the building blocks of any Angular application, so you will get plenty of hands-on practice with this material. Next, you’ll ramp things up by stepping into modules, routing, and RXJS, where you’ll learn how to declaratively manage data and dive deep into observables. In the latter half of the course, you’ll cover reactive and template forms (crucial for handling user data), as well as custom validators and setting up authentication with Firebase. By the end of this course, you'll be able to design large, scalable applications, be able to develop rich interactive components, and you’ll really understand how Angular works under the hood.

20hrs
Beginner
132 Playgrounds
4 Quizzes

By the end, you’ll have hands-on experience, plus a project for your professional portfolio.

Happy learning!

Continue reading about Angular 2+ and front-end development#

Explore the key concepts regarding Angular in more detail through our interactive blogs/courses/paths on Educative:

Explore the key concepts regarding Angular in more detail through our interactive course on Educative:

Cover
Learning Angular

In this course, you will learn Angular, which is currently among the top JavaScript frameworks. More developers are now seeking the best way to get started with this flexible and secure framework. You will learn how to achieve cross-platform high performance with the latest web techniques. You will begin with the basics of an Angular application, including its project structure. Then, you will learn about the basics of TypeScript and its importance in type checking. Next, you will cover how to create components and use pipes and directives in Angular. You’ll progress to cover event handling, navigation, and routing. You will end this course by learning how to create unit tests and debug an Angular application to prepare it for production. After completing this course, you will gain essential skills to develop apps by harnessing the power of the Angular command-line interface (CLI), write unit tests, style your apps by following the Material Design guidelines, and finally, deploy them to a hosting provider.

70hrs
Intermediate
137 Playgrounds
14 Quizzes

Ready to dive deeper? Explore our interactive skills path on Educative to build scalable Angular applications, from beginner concepts to advanced techniques.

Cover
Become an AngularJS Developer

Angular is a JavaScript framework for building single-page client applications using HTML and TypeScript. If you are not using Angular yet, you're missing out on why Angular can be instrumental in landing a good position in the development industry. This Skill Path will help you learn how to create and deploy scalable applications with Angular. You will also learn automated testing using the Angular framework, state management, and animations in Angular. By the end, you'll have job-ready skills to use Angular in your next project confidently.

143hrs
Beginner
302 Playgrounds
30 Quizzes

Frequently Asked Questions

What are the best practices for Angular routing?

  • Organize routes in a modular way, using feature modules to group related routes together.
  • Implement route guards to manage access to specific routes based on conditions like user authentication.
  • Prefer relative paths over absolute paths for better flexibility and maintainability.

What is the difference between static and dynamic routing in Angular?

Static routing occurs when paths are manually updated by the user or administrator. Dynamic routing refers to paths that are updated automatically.

Do I need Angular routing?

Yes, you need Angular routing if your application requires navigation between multiple views or pages without reloading the whole app.

It is essential for single-page applications (SPAs) where users can navigate between components via URLs, improving the user experience by making navigation faster and smoother.

What is the routing state in Angular?

In Angular, the routing state represents the current active route and all the information associated with it, such as URL parameters, query parameters, and the data fetched through resolvers.

It allows the application to maintain and track the user’s navigation history and dynamically update the UI as the user navigates between routes.

What are the three types of routing protocols?

The three primary routing strategies in Angular are:

  • HashLocationStrategy: Appends a hash (#) to the URL for routing, ensuring compatibility with older browsers.
  • PathLocationStrategy: Uses the browser’s standard URL path without a hash, enabling cleaner URLs.
  • PushState: Part of the HTML5 History API, allowing for natural and clean URLs without reloading the page.

What are the three pillars of Angular routing?

The three pillars of Angular routing are:

  • RouterModule: Configures and manages the application’s routes.
  • RouterOutlet: Acts as a placeholder in the template where routed components are rendered.
  • RouterLink: A directive that enables navigation between routes without a full page reload, improving performance and user experience in SPAs.

Written By:
Ryan Thelin

Free Resources