Mapping Out the Navigation Layout

We'll assemble a navigation structure with several layers, similar to what an e-commerce site or a learning platform might use.

Bootstrap will provide the styling foundation for the menu system we're about to construct.

This is a hands-on walkthrough: the focus is on configuring the Angular Router through a real, working example. We'll cover the core routing scenarios that show up again and again in production applications.

What We'll Cover

These routing features will be combined to build the complete menu system:

  • Initial Router Setup
  • Child Routes
  • Nested Child Routes
  • Auxiliary Routes
  • Nested Auxiliary Routes

This is the second installment in our series on the Angular Router. The full series includes:

Angular Router Fundamentals: Child Routes, Auxiliary Routes, Master-Detail

Angular Router: A Complete Guide (with Bootstrap)

Let's move on to building the navigation menu.

The Target Design

Here's a visual of the menu system we're aiming for:

Bootstrap Angular menu

The layout contains these pieces:

  • a persistent top-level menu
  • a trail of breadcrumbs for navigation context
  • A secondary sidebar that appears only after the Courses section is entered
  • navigation between the course category listing and each individual category, with the sidebar contents updating accordingly

Building the Top Menu

Because the top menu needs to be present at all times, it belongs in the application's root component. Every view will render below it.

Alternatively, the menu markup can live in a dedicated top-menu.component.ts file, which is useful if the menu grows large. Note the routerLink directives that point to home, about, and courses.

The router-outlet element is the key: whatever page we're on, its main content gets injected right there. When we start, nothing occupies the sidebar space—it should only exist when the Courses link is activated. Next, we define the router config for this top-level navigation.

Top Menu Router Configuration

This starting config handles all the routes the top menu exposes:

The home, about, and courses paths each map to a single component. There's also a redirect handling the root path (empty string) and a catch-all with the ** wildcard for unmatched URLs.

This initial setup already covers the home page, gracefully handles bad URLs, and lays down the main navigation items.

With the header in place, these are next on the list:

  • a sidebar that lists the course categories, shown only when Courses is selected
  • a set of category cards appearing in the page's main content area

The Courses Categories View

This is what CoursesComponent should render:

Bootstrap Angular menu

The screen is made up of a few distinct parts:

  • a large 'jumbotron' heading that reads "Course Categories!"
  • a grid of course category cards, three per row
  • a sidebar with navigation shortcuts to each category

The content under the top menu fills the router outlet, as expected. This view, however, has its own internal routing logic we'll need to manage.

Adding the Sidebar

To support the sidebar, CoursesComponent needs to provide its own outlets:

  • a primary outlet inside the component to display the list of course categories
  • an auxiliary outlet to host the sidebar navigation

Before getting into the details, we need to use Child Routes. A solid introduction to child routes is available in this earlier post.

Let's look at this routing scenario in more detail.

Nested Route for the Category Cards

In the CoursesComponent template, the implementation looks like this:

Notice the multiple router-outlet elements: this component itself is loaded inside a top-level outlet, yet defines its own outlets. This is the nested route pattern.

We'll get to the auxiliary outlet shortly. For now, our aim is to configure the router so the category cards component populates the unnamed outlet.

Nested Route Configuration

The router config needed to load both the courses view and the category cards is as follows:

Navigating to /courses triggers this behavior:

  • CoursesComponent is rendered inside the main router-outlet located below the top menu
  • Within CoursesComponent, the <router-outlet></router-outlet> gets replaced by CourseCardsComponent

This is progress, yet the sidebar remains. To populate it, we'll rely on the auxiliary route concept.

Secondary Routes in Practice

When different sections of the screen change independently based on the URL, we are dealing with secondary (or auxiliary) routes. In the router configuration, this translates to a route that uses an outlet name other than the default one.

The main route is tied to the unnamed outlet (<router-outlet></router-outlet>). Beyond that, a single routing level can host any number of additional outlets, provided each one carries a distinct name.

Building the Side Menu Component

We'll place the side menu inside a sidemenu-named outlet. The menu's links should adapt based on the current URL. For instance, landing on the Courses section should show a list of Course Categories, but drilling into a specific category should display sub-category links.

To pull this off, the SideMenuComponent needs access to the current URL to decide which data to load. The example below is a stripped-down version; in a real scenario you'd inject a service or use a Router Resolver to fetch data before the component initializes.

More importantly, note how the side menu listens for URL changes—this reactivity is what makes it flexible enough to mirror the main content.

Setting Up a Nested Secondary Route for the Menu

Here's a router configuration that mounts the side menu:

Navigating to /courses will now yield the following results for the courses.component.html template:

  • The CourseCardsComponent fills the default (primary) router-outlet.
  • The SideMenuComponent appears inside the outlet labeled sidemenu.

A pattern starts to emerge: the router handles a wide array of navigation contexts using just a few core concepts: routes, outlets, and child routes.

We can push this pattern further by adding another layer of navigation.

Creating the Course Categories Component

Suppose the user clicks on Development while inside the Courses area. We want two things to happen simultaneously:

  • The primary outlet inside Courses swaps its content for a new component that shows the category's contents, such as a list of sub-categories.

  • The side menu also updates to display links specific to the Development category.

While the URL changes, two separate regions on the page react independently.

One More Level of Nesting

If the user clicks Development, the URL shifts to /courses/development. A click on IT & Software would change it to /courses/it-software, and so on.

For these URLs, we'll display a fresh CoursesCategoryComponent in the primary outlet of the Courses page. The configuration for that looks like this:

Note the :id placeholder—it captures the dynamic segment of the URL, resulting in values like development or it-software.

Drilling into a Sub-Section

To fetch data for a particular category, the Development card can trigger a navigation like this:

Right away, you'll notice a limitation: this navigation won't inform the side menu about the change—more on that below.

While the main body would correctly render CoursesCategoryComponent, the side menu wouldn't budge.

Adapting the Side Menu to the URL

In our current setup, the side menu initializes when the user enters the Courses page via the top menu and then stays static. To ensure it reacts to the category-specific URL—for example by showing development sub-sections—the router configuration needs another piece:

We've now added a navigation route for the side menu outlet too. When the browser lands on /courses/development, we'd anticipate the SideMenuComponent picking up the development segment via a subscription to its params observable.

Alas, that's not how it works—let's dig into why.

A Side Menu's Perspective

From the side menu's viewpoint, navigating from /courses to /courses/development only changes the primary route. Content inside the sidemenu outlet remains untouched.

To get the side menu to change, a separate navigation within the sidemenu outlet is required. Think of it as having two parallel browser windows on the same page: one drives the main content, the other drives the side menu.

Triggering Side Menu Navigation

To initiate a navigation for the side menu, we might still do it from the template—but for complicated navigation, we can inject the router and use its imperative navigation API.

Template-based navigation is fine, but leveraging TypeScript auto-completion when calling the router's methods can be a lifesaver. It effectively serves as a real-time documentation, suggesting the valid parameters as you type.

To get started, we'll swap the routerLink directive on the category card for a click handler:

Now the navigation is fired via a method call, not directly in the markup. Let's look at that method.

Here is how we can pull off the navigation in code, which could also be recreated in the template using router commands:

Notice we're issuing one router navigation command that targets two outlets at once: the primary and the sidemenu.

Here is what happens in each one:

  • Because we provide a relativeTo property with the current active route, the navigation is relative to the route we're already on.

  • The primary outlet's navigation path (/courses/development) loads CoursesCategoryComponent into the main Courses template.

  • The auxiliary sidemenu outlet's navigation (via the same path) allows the SideMenuComponent to refresh based on the new segment.

Since the SideMenuComponent is subscribed to the route.params observable, the click on the Development card will now cause development to be written to the console via console.log.

Now that the side menu is notified of URL transitions, it can dynamically swap its content when needed.

What a Multi-Outlet URL Looks Like

If you preview your browser's address bar after triggering the nav call above, you'd see:

/courses/(development//sidemenu:development)

To break that URL down:

  • the courses segment is currently active
  • within it, the primary route points to /courses/development
  • the auxiliary route for sidemenu is set to the development child segment

Final Takeaways

The Angular router's configuration API is remarkably lean but versatile. With a handful of building blocks—routes, outlets, and child routes—we can support a broad range of situations.

Interestingly, a lot of typical navigation ideas (like 'nested routes') don't map 1:1 to any single term in the router's configuration. A nested view might simply be a child route with a different component; likewise, an auxiliary route is just a standard route assigned to a named outlet, inheriting all the properties of a primary route.

Starting from these primitive concepts, it's easy to arrange most navigation flows an app might need: top-level menus, hierarchical pages, sidebars, and more.

We hope this guiding walkthrough of the Angular Router Table proves helpful--happy navigating!

Questions or feedback? Drop them in the comments below and we'll jump in.

Want to catch upcoming posts on the Angular Router and related topics? Subscribe to our newsletter:

To go much deeper into all the Angular Router's advanced capabilities, check out the Angular Router In Depth course for exhaustive detail.

Getting started with Angular and looking for a foundation course? See the Angular for Beginners Course:

Angular Router: A Complete Guide — figure 3

Further Reading on Angular

If you found this article useful, you might also like these popular posts: