# Next.js Bootstrap Navbar

The Next.js Bootstrap navbar in AdminKit: notifications, messages, color mode and user menus on every dashboard page, and how to change or add them.

Every dashboard page in [AdminKit for Next.js](/nextjs/) has the same top bar: the sidebar toggle and, on the right, menus for notifications, messages, the color mode and the user's account. It is in the free and PRO versions. Each menu is a small component of its own, built on the [Dropdown](/nextjs/docs/components/dropdowns/) client component, so you can change or remove one without touching the others.

The bar is `Navbar`, in `src/components/navbar/navbar.tsx`, and the menus are in the same folder. `DashboardLayout` renders it for every page of the `(dashboard)` route group, through `src/app/(dashboard)/layout.tsx`. So it stays in place while the page below it changes.

`navbar.tsx` is a client component, a file that starts with `"use client"`. It has to be: its sidebar toggle reads state from `useLayout()`, and server components have no state. The menu files that only render markup have no `"use client"` of their own. `Navbar` imports them, so they become client code too. All of it is still prerendered: the static HTML of each page already contains the navbar.

## Example

This is the notifications menu, `src/components/navbar/navbar-notifications.tsx`, with two items of your own. Like every menu, it is a `<li>` in the list on the right of the bar:

```html
<nav class="navbar navbar-expand navbar-light navbar-bg" aria-label="Top bar">
  <div class="navbar-collapse collapse">
    <ul class="navbar-nav navbar-align">
      <li class="nav-item dropdown">
        <a class="nav-icon dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
          <div class="position-relative">
            <span class="visually-hidden">Notifications</span>
            <i class="align-middle" data-lucide="bell"></i>
            <span class="indicator">2</span>
          </div>
        </a>
        <div class="dropdown-menu dropdown-menu-lg dropdown-menu-end py-0">
          <div class="dropdown-menu-header">2 New Notifications</div>
          <div class="list-group">
            <a href="#" class="list-group-item">
              <div class="row g-0 align-items-center">
                <div class="col-2"><i class="text-danger" data-lucide="circle-alert"></i></div>
                <div class="col-10">
                  <div class="text-dark">Payment failed</div>
                  <div class="text-body-secondary small mt-1">The card of Acme Inc. was declined.</div>
                  <div class="text-body-secondary small mt-1">30m ago</div>
                </div>
              </div>
            </a>
            <a href="#" class="list-group-item">
              <div class="row g-0 align-items-center">
                <div class="col-2"><i class="text-success" data-lucide="user-plus"></i></div>
                <div class="col-10">
                  <div class="text-dark">New member</div>
                  <div class="text-body-secondary small mt-1">Christina joined your team.</div>
                  <div class="text-body-secondary small mt-1">2h ago</div>
                </div>
              </div>
            </a>
          </div>
          <div class="dropdown-menu-footer">
            <a href="#" class="text-body-secondary">Show all notifications</a>
          </div>
        </div>
      </li>
    </ul>
  </div>
</nav>
```
```tsx code title="src/components/navbar/navbar-notifications.tsx"
import { Bell, CircleAlert, UserPlus } from "lucide-react";
import {
  Dropdown,
  DropdownMenu,
  DropdownToggle,
} from "@/components/bootstrap/dropdown";

const notifications = [
  {
    icon: <CircleAlert className="text-danger" />,
    title: "Payment failed",
    text: "The card of Acme Inc. was declined.",
    time: "30m ago",
  },
  {
    icon: <UserPlus className="text-success" />,
    title: "New member",
    text: "Christina joined your team.",
    time: "2h ago",
  },
];

export function NavbarNotifications() {
  return (
    <Dropdown as="li" className="nav-item dropdown" display="static">
      <DropdownToggle className="nav-icon dropdown-toggle">
        <div className="position-relative">
          <span className="visually-hidden">Notifications</span>
          <Bell className="align-middle" />
          <span className="indicator">{notifications.length}</span>
        </div>
      </DropdownToggle>
      <DropdownMenu className="dropdown-menu-lg dropdown-menu-end py-0">
        <div className="dropdown-menu-header">
          {notifications.length} New Notifications
        </div>
        <div className="list-group">
          {notifications.map((notification) => (
            <a href="#" className="list-group-item" key={notification.title}>
              <div className="row g-0 align-items-center">
                <div className="col-2">{notification.icon}</div>
                <div className="col-10">
                  <div className="text-dark">{notification.title}</div>
                  <div className="text-body-secondary small mt-1">{notification.text}</div>
                  <div className="text-body-secondary small mt-1">{notification.time}</div>
                </div>
              </div>
            </a>
          ))}
        </div>
        <div className="dropdown-menu-footer">
          <a href="#" className="text-body-secondary">Show all notifications</a>
        </div>
      </DropdownMenu>
    </Dropdown>
  );
}
```

The template has the count (`4`) and the header ("4 New Notifications") as fixed text. Here they come from the data.

## Parts

| Part | File | Description |
| --- | --- | --- |
| Sidebar toggle | `navbar.tsx` | `toggleSidebar` from `useLayout()`: opens the sidebar sheet below lg; from lg up it hides the sidebar (free) or switches to the compact sidebar (PRO) |
| `NavbarNotifications` | `navbar-notifications.tsx` | Bell icon with a count and a list of notifications |
| `NavbarMessages` | `navbar-messages.tsx` | Message icon and a list of messages with avatars |
| `NavbarColorMode` | `navbar-color-mode.tsx` | Light, dark and auto (`"use client"`, it uses `useColorMode`); see [Dark mode](/nextjs/docs/customization/dark-mode/#toggle) |
| `NavbarUser` | `navbar-user.tsx` | The account menu: Profile, Analytics, Settings & Privacy, Help Center, Log out |
| Search (PRO) | `navbar.tsx` | A search field from the sm breakpoint up; connect it to your own search |
| `NavbarMenus` (PRO) | `navbar-menus.tsx` | The "Mega Menu" and "Resources" menus, from lg up |
| `NavbarLanguages` (PRO) | `navbar-languages.tsx` | A flag menu with four languages; the items link to `#`, there are no translations |
| Fullscreen (PRO) | `navbar.tsx` | A button from lg up that puts the page in fullscreen and back |

The free version's user menu shows a settings icon below the sm breakpoint, and the avatar with the name from sm up. In AdminKit PRO the toggle is the avatar, and the sidebar has a user block as well.

The color mode toggle shows the auto icon in the HTML that Next.js builds, because the stored mode is only known in the browser. After hydration, when the browser takes over that HTML, it shows the current mode.

## Change the navbar

- **Remove a menu**: delete its line in `navbar.tsx`, and its file if nothing else imports it.
- **Change a menu**: edit its data array and markup, as in the example.
- **Add a menu**: return `<Dropdown as="li" class="nav-item dropdown" display="static">` with a `DropdownToggle` (`nav-icon` for an icon, `nav-link` for text) and a `DropdownMenu` with `dropdown-menu-end`, and add it to the list in `navbar.tsx`.

`display="static"` keeps the menu where the navbar CSS puts it, as Bootstrap does in a navbar.

The static HTML holds the data of the menus as it was at build time. To show notifications or messages from your API, fetch them in the browser, in the menu component. That component then needs `"use client"`, because it uses `useState` and `useEffect`. Fetching on the server for each visit is not an option: with the static export, no server runs after the build.

## Links

Links to pages of the app use `next/link` with `href`. It prefetches the page and adds the base path when the app is served from a sub-folder:

```tsx
import Link from "next/link";

<Link className="dropdown-item" href="/pages-profile">
  <User className="align-middle me-1" /> Profile
</Link>
```

The other items are `<a href="#">` placeholders. Images, such as the avatar and the flags, are plain `<img>` elements, so they use `asset("/img/...")` from `src/lib/site.ts` to add the base path.

## Accessibility

- The bar is a `<nav>` with `aria-label="Top bar"`.
- The sidebar toggle has `aria-controls="sidebar"`, `aria-expanded`, and the label "Collapse sidebar" or "Expand sidebar".
- Icon toggles have a name: `visually-hidden` text for notifications and `aria-label="Messages"` for messages. The color mode toggle has `aria-label="Color mode (dark)"`, with the current mode, and its items have `aria-pressed`.
- The dropdowns open with a click, Enter, or Arrow Down and Arrow Up, which also move through the items; Escape closes them and returns the focus to the toggle.
- In AdminKit PRO, the search field and the fullscreen button have `aria-label`s.

## Related

- [React Bootstrap navbar](/react/docs/navigation/navbar/): the same navbar in AdminKit for React (Vite)
- [Bootstrap 5 navbar](/bootstrap/docs/navigation/navbar/): the navbar of the HTML version
- [Sidebar layout](/nextjs/docs/layout/sidebar/): the dashboard layout and the sidebar toggle
- [Analytics dashboard demo](https://nextjs-demo.adminkit.io/)

---

Canonical HTML page: https://adminkit.io/nextjs/docs/navigation/navbar/

Site overview for agents: https://adminkit.io/llms.txt (expanded: https://adminkit.io/llms-full.txt) · Documentation: https://adminkit.io/bootstrap/docs/ · Questions: support@adminkit.io
