I've built a lot of Angular admin dashboards over the years and the navbar is always where people get stuck. Either they can't get the mobile toggle working properly, the active route detection is broken, or they've wired up Bootstrap's JS in a way that fights Angular's change detection.
This guide shows you how to do it properly with Angular 21 standalone components and Bootstrap 5.3.
Before You Start
Make sure Bootstrap is installed in your Angular project:
npm install bootstrap bootstrap-icons
Add to angular.json under styles and scripts:
"styles": [ "node_modules/bootstrap/dist/css/bootstrap.min.css", "node_modules/bootstrap-icons/font/bootstrap-icons.css", "src/styles.scss" ], "scripts": [ "node_modules/bootstrap/dist/js/bootstrap.bundle.min.js" ]
That's all you need. Don't install @ng-bootstrap/ng-bootstrap unless you specifically need Angular-native Bootstrap components. For a navbar, plain Bootstrap CSS + JS is simpler and works fine.
The Basic Navbar Component
Create the component:
ng generate component navbar --standalone
Here's the HTML template. The key things to note are routerLink, routerLinkActive and the mobile toggle handling:
<!-- navbar.component.html --> <nav class="navbar navbar-expand-lg bg-white border-bottom sticky-top shadow-sm"> <div class="container-fluid"> <!-- Brand --> <a class="navbar-brand fw-bold d-flex align-items-center gap-2" routerLink="/" style="color:#fd4766;"> <span class="bg-danger text-white px-2 py-1 rounded" style="font-size:0.8rem;">BP</span> BootstrapPlanet </a> <!-- Mobile Toggle --> <button class="navbar-toggler border-0" type="button" (click)="isCollapsed = !isCollapsed" [attr.aria-expanded]="!isCollapsed" aria-label="Toggle navigation"> <span class="navbar-toggler-icon"></span> </button> <!-- Nav Links --> <div class="navbar-collapse" [class.collapse]="isCollapsed"> <ul class="navbar-nav me-auto mb-2 mb-lg-0"> <li class="nav-item"> <a class="nav-link" routerLink="/components" routerLinkActive="active" (click)="closeNav()"> Components </a> </li> <li class="nav-item"> <a class="nav-link" routerLink="/tutorials" routerLinkActive="active" (click)="closeNav()"> Tutorials </a> </li> <li class="nav-item"> <a class="nav-link" routerLink="/angular" routerLinkActive="active" (click)="closeNav()"> Angular </a> </li> <!-- Dropdown --> <li class="nav-item dropdown"> <a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown"> More </a> <ul class="dropdown-menu"> <li> <a class="dropdown-item" routerLink="/vs" routerLinkActive="active" (click)="closeNav()"> Comparisons </a> </li> <li> <a class="dropdown-item" routerLink="/templates" routerLinkActive="active" (click)="closeNav()"> Templates </a> </li> </ul> </li> </ul> <!-- Right side --> <div class="d-flex align-items-center gap-2"> <a routerLink="/search" class="btn btn-sm btn-outline-secondary"> <i class="bi bi-search"></i> </a> <a href="https://lettstartdesign.com" target="_blank" class="btn btn-sm text-white fw-semibold px-3" style="background:#fd4766;"> Templates ↗ </a> </div> </div> </div> </nav>
Now the component TypeScript:
// navbar.component.ts import { Component } from '@angular/core' import { RouterLink, RouterLinkActive } from '@angular/router' import { CommonModule } from '@angular/common' @Component({ selector: 'app-navbar', standalone: true, imports: [RouterLink, RouterLinkActive, CommonModule], templateUrl: './navbar.component.html' }) export class NavbarComponent { isCollapsed = true closeNav() { this.isCollapsed = true } }
The isCollapsed boolean controls mobile visibility. When a link is clicked, closeNav() collapses it back. Simple and reliable — no Bootstrap JS events fighting Angular's change detection.
Active Route Highlighting
routerLinkActive="active" does exactly what you expect — adds the active CSS class when the route matches.
One gotcha: the home route (/) will match every route because every URL starts with /. Fix it with routerLinkActiveOptions:
<a class="nav-link" routerLink="/" routerLinkActive="active" [routerLinkActiveOptions]="{ exact: true }"> Home </a>
Without exact: true your home link will always appear active no matter what page you're on. I've seen this trip people up more times than I can count.
Closing the Dropdown on Route Change
If you're using Bootstrap's dropdown in the navbar and navigating via routerLink, you might find the dropdown stays open after clicking a link. Fix this by subscribing to router events:
import { Component, OnInit } from '@angular/core' import { Router, NavigationEnd, RouterLink, RouterLinkActive } from '@angular/router' import { CommonModule } from '@angular/common' import { filter } from 'rxjs/operators' @Component({ selector: 'app-navbar', standalone: true, imports: [RouterLink, RouterLinkActive, CommonModule], templateUrl: './navbar.component.html' }) export class NavbarComponent implements OnInit { isCollapsed = true constructor(private router: Router) {} ngOnInit() { // Close navbar on any navigation this.router.events .pipe(filter(event => event instanceof NavigationEnd)) .subscribe(() => { this.isCollapsed = true // Close any open Bootstrap dropdowns document.querySelectorAll('.dropdown-menu.show').forEach(el => { el.classList.remove('show') }) document.querySelectorAll('.dropdown-toggle.show').forEach(el => { el.classList.remove('show') el.setAttribute('aria-expanded', 'false') }) }) } closeNav() { this.isCollapsed = true } }
This subscribes to NavigationEnd events and closes both the mobile navbar and any open dropdowns. Cleaner than managing state in individual link click handlers.
Dark Navbar Variant
For a dark themed navbar — common in admin dashboards — just change the background and add data-bs-theme="dark":
<nav class="navbar navbar-expand-lg sticky-top" data-bs-theme="dark" style="background:#0d0d0d;border-bottom:1px solid #222;"> <!-- everything else stays the same --> </nav>
Bootstrap 5.3's data-bs-theme="dark" automatically inverts link colors, the toggler icon and dropdown menus. No extra CSS needed.
Using the Navbar Component
Add to your app.component.html:
<app-navbar /> <router-outlet />
And import it in your app.component.ts:
import { NavbarComponent } from './navbar/navbar.component' @Component({ standalone: true, imports: [RouterOutlet, NavbarComponent], ... })
A Note on ng-bootstrap
You might be wondering why I'm not using ng-bootstrap here. Honest answer: for a navbar, you don't need it. ng-bootstrap is useful for complex components like datepickers, typeaheads and modals where you want deep Angular integration. For a navbar where Bootstrap's JS handles the toggle, plain Bootstrap CSS + JS is simpler and easier to maintain.
If you're building a component library or a large application where you want everything to feel native Angular, ng-bootstrap is worth it. But for most projects — especially admin dashboards — I find the overhead not worth it.
The navbar I've shown here has been my go-to for Angular projects for the past few years. It's simple, it works, and it doesn't fight Angular's change detection.
Frequently Asked Questions
Related
Need a Full Angular + Bootstrap Admin Dashboard?
Marvel Angular Dashboard — Angular 21 + Bootstrap 5 with 50+ components and dark mode.
Browse Templates →Use code FIRST30 for 30% off.