# Next.js Bootstrap Dropdowns

Next.js Bootstrap dropdown menus that server component pages can render as is: split buttons, alignment, auto close, keyboard support and next/link items.

Dropdowns keep a toolbar or a navbar tidy: a short list of actions or links waits behind one button until the user needs it. AdminKit builds them from three components: `Dropdown`, `DropdownToggle` and `DropdownMenu`. They open Bootstrap's dropdown menus from React state, without Bootstrap's JavaScript. The Floating UI library positions the menu 2px from the toggle and flips it to stay in view. The components are in the free and PRO versions of [AdminKit for Next.js](/nextjs/).

In the App Router, pages are server components: they render on the server and have no state. A menu needs state to open and close, so the three components are client components: `dropdown.tsx` starts with `"use client"`. `Dropdown` keeps the open state itself, so a dropdown with plain links needs no `"use client"` of its own. It can sit in a server component page, or in a component like `ProjectActions` below. You only need your own client component for event handlers, such as an `onClick` on a menu item, because a server component cannot pass functions.

## Example

`Dropdown` adds no class itself, so give it `dropdown` or `btn-group`. The toggle needs `dropdown-toggle` for its caret:

```html
<div class="dropdown">
  <button class="btn btn-primary dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">
    Actions
  </button>
  <ul class="dropdown-menu">
    <li><a class="dropdown-item" href="#">Edit</a></li>
    <li><a class="dropdown-item" href="#">Duplicate</a></li>
    <li><hr class="dropdown-divider"></li>
    <li><a class="dropdown-item" href="#">Archive</a></li>
  </ul>
</div>
```
```tsx code title="src/components/project-actions.tsx"
import {
  Dropdown,
  DropdownMenu,
  DropdownToggle,
} from "@/components/bootstrap/dropdown";

export function ProjectActions() {
  return (
    <Dropdown className="dropdown">
      <DropdownToggle as="button" className="btn btn-primary dropdown-toggle">
        Actions
      </DropdownToggle>
      <DropdownMenu as="ul">
        <li><a className="dropdown-item" href="#">Edit</a></li>
        <li><a className="dropdown-item" href="#">Duplicate</a></li>
        <li><hr className="dropdown-divider" /></li>
        <li><a className="dropdown-item" href="#">Archive</a></li>
      </DropdownMenu>
    </Dropdown>
  );
}
```

The menu is in the HTML that Next.js builds, hidden until it gets `.show`. Floating UI runs only in the browser, once the menu opens. So the first render in the browser matches the server's.

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `as` | `ElementType` | `"div"` | The wrapper element, such as `"li"` in a navbar |
| `display` | `"dynamic" \| "static"` | `"dynamic"` | `"dynamic"` positions the menu with Floating UI; `"static"` leaves it where the CSS puts it, as in the navbar |
| `strategy` | `"absolute" \| "fixed"` | `"absolute"` | `"fixed"` positions the menu relative to the window, so containers that clip or scroll do not cut it off |
| `autoClose` | `boolean \| "inside" \| "outside"` | `true` | Which clicks close the menu, see [Auto close](#auto-close) |
| `drop` | `"down" \| "up" \| "start" \| "end"` | from the class | The side the menu opens on; read from `dropup`, `dropend` or `dropstart` in `className` |

`Dropdown` passes its other props, such as `className`, to the wrapper. The components are in `src/components/bootstrap/dropdown.tsx`.

- `DropdownToggle` renders `<a href="#" role="button">`, or a `<button type="button">` with `as="button"`. It sets `aria-expanded` and `aria-controls`, gets `.show` while open, and still calls your `onClick` and `onKeyDown`.
- `DropdownMenu` adds `.dropdown-menu` and takes `as` (`"div"` by default) and `align` (`"start"` or `"end"`; `.dropdown-menu-end` sets `"end"`).

## Split button

A split button keeps the main action one click away and puts the alternatives in the menu. Put a regular button and a toggle with `dropdown-toggle-split` in a `btn-group`. The regular button has an `onClick`, so this one is a client component:

```html
<div class="btn-group">
  <button type="button" class="btn btn-success">Publish</button>
  <button type="button" class="btn btn-success dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" aria-expanded="false">
    <span class="visually-hidden">More publish options</span>
  </button>
  <ul class="dropdown-menu">
    <li><a class="dropdown-item" href="#">Schedule</a></li>
    <li><a class="dropdown-item" href="#">Save as draft</a></li>
  </ul>
</div>
```
```tsx code title="src/components/publish-button.tsx"
"use client";

import {
  Dropdown,
  DropdownMenu,
  DropdownToggle,
} from "@/components/bootstrap/dropdown";

export function PublishButton({ onPublish }: { onPublish: () => void }) {
  return (
    <Dropdown className="btn-group">
      <button type="button" className="btn btn-success" onClick={onPublish}>
        Publish
      </button>
      <DropdownToggle as="button" className="btn btn-success dropdown-toggle dropdown-toggle-split">
        <span className="visually-hidden">More publish options</span>
      </DropdownToggle>
      <DropdownMenu as="ul">
        <li><a className="dropdown-item" href="#">Schedule</a></li>
        <li><a className="dropdown-item" href="#">Save as draft</a></li>
      </DropdownMenu>
    </Dropdown>
  );
}
```

`onPublish` is a function, and a server component cannot pass functions. So the component that renders `PublishButton` must be a client component too, or `PublishButton` has to define the handler itself.

## Alignment and direction

To move the menu, use Bootstrap's classes, which the components read. `dropdown-menu-end` on the menu aligns it to the end of the toggle. `dropup`, `dropend` or `dropstart` on the wrapper opens it above, to the right or to the left. The `align` and `drop` props do the same without the classes.

```html
<div class="btn-group me-2">
  <button type="button" class="btn btn-secondary dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">End aligned</button>
  <ul class="dropdown-menu dropdown-menu-end">
    <li><a class="dropdown-item" href="#">Menu item</a></li>
    <li><a class="dropdown-item" href="#">Menu item</a></li>
  </ul>
</div>
<div class="btn-group dropup">
  <button type="button" class="btn btn-secondary dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">Dropup</button>
  <ul class="dropdown-menu">
    <li><a class="dropdown-item" href="#">Menu item</a></li>
    <li><a class="dropdown-item" href="#">Menu item</a></li>
  </ul>
</div>
```
```tsx code
<>
  <Dropdown className="btn-group me-2">
    <DropdownToggle as="button" className="btn btn-secondary dropdown-toggle">End aligned</DropdownToggle>
    <DropdownMenu as="ul" className="dropdown-menu-end">
      <li><a className="dropdown-item" href="#">Menu item</a></li>
      <li><a className="dropdown-item" href="#">Menu item</a></li>
    </DropdownMenu>
  </Dropdown>
  <Dropdown className="btn-group dropup">
    <DropdownToggle as="button" className="btn btn-secondary dropdown-toggle">Dropup</DropdownToggle>
    <DropdownMenu as="ul">
      <li><a className="dropdown-item" href="#">Menu item</a></li>
      <li><a className="dropdown-item" href="#">Menu item</a></li>
    </DropdownMenu>
  </Dropdown>
</>
```

A table or a scrolling card can cut off a menu. Use `strategy="fixed"` there, as the menus of the tasks board and the AI chat history in AdminKit PRO do.

## In the navbar

A navbar menu is an `li` with `display="static"`, so the navbar's CSS places it. Use `Link` from `next/link` for pages of the app, as `navbar-user.tsx` does:

```tsx title="src/components/navbar/navbar-account.tsx"
import Link from "next/link";
import {
  Dropdown,
  DropdownMenu,
  DropdownToggle,
} from "@/components/bootstrap/dropdown";

export function NavbarAccount() {
  return (
    <Dropdown as="li" className="nav-item dropdown" display="static">
      <DropdownToggle className="nav-link dropdown-toggle">Account</DropdownToggle>
      <DropdownMenu className="dropdown-menu-end">
        <Link className="dropdown-item" href="/pages-profile">Profile</Link>
        <Link className="dropdown-item" href="/pages-settings">Settings</Link>
        <div className="dropdown-divider" />
        <a className="dropdown-item" href="#">Log out</a>
      </DropdownMenu>
    </Dropdown>
  );
}
```

A click on a `Link` in the menu closes the menu and navigates in the browser, without a full page load.

## Auto close

`autoClose` decides which clicks close an open menu:

| `autoClose` | A click on an item | A click outside |
| --- | --- | --- |
| `true` (default) | Closes the menu | Closes the menu |
| `"inside"` | Closes the menu | Keeps it open |
| `"outside"` | Keeps it open | Closes the menu |
| `false` | Keeps it open | Keeps it open |

With `autoClose={false}`, only the toggle and Escape close the menu. A link or button inside a `<form>` in the menu never closes it.

## Menu content

Bootstrap's classes work as in HTML: `.dropdown-item` on a link or button, `.dropdown-item-text`, `.dropdown-header`, `.dropdown-divider`, and `.active` or `.disabled` on an item. [Bootstrap's dropdown docs](https://getbootstrap.com/docs/5.3/components/dropdowns/) list them all.

## Accessibility

- `aria-expanded` and `aria-controls` on the toggle point at the menu, whose `id` comes from `useId`.
- Arrow Down or Arrow Up on the toggle opens the menu on its first or last item. In the menu, the arrows move between the enabled items.
- Escape closes the menu and puts the focus back on the toggle.
- Give an icon-only toggle a text for screen readers: a `visually-hidden` span or an `aria-label`.
- Mark the current page with `.active` and `aria-current="page"`.

## Related

- [React Bootstrap dropdowns](/react/docs/components/dropdowns/): the same components in AdminKit for React (Vite)
- [Bootstrap 5 dropdowns](/bootstrap/docs/components/dropdowns/): the dropdowns of the HTML version
- [Offcanvas](/nextjs/docs/components/offcanvas/): a side panel for longer menus
- [Buttons demo](https://nextjs-demo.adminkit.io/ui-buttons): button dropdowns in every theme color

---

Canonical HTML page: https://adminkit.io/nextjs/docs/components/dropdowns/

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
