# Next.js Bootstrap Dark Mode

Next.js Bootstrap dark mode in AdminKit: light, dark and auto, with no flash of the wrong mode in the static export, plus a hook and a navbar toggle.

Light, dark or auto: in [AdminKit for Next.js](/nextjs/), your users pick the mode, and auto follows their operating system. All three are in the free version and in AdminKit PRO. Switching changes the page in place, without a reload. The page never shows the wrong mode first, not even in the static export, where every page is a plain HTML file built ahead of time.

The modes are [Bootstrap's color modes](https://getbootstrap.com/docs/5.3/customize/color-modes/). One stylesheet holds the light and the dark mode. The `data-bs-theme` attribute on `<html>` picks one: `light` or `dark`.

## Light, dark and auto

Each user's choice is stored in `localStorage` as `adminkit_config_colorMode`: `light`, `dark` or `auto`. With auto, or when nothing is stored, the page follows the operating system setting (`prefers-color-scheme`). If that setting changes while the page is open, the page follows along.

The static HTML is built before anyone visits, so it cannot know the stored mode. A small script in `<head>` fills that gap. It sets `data-bs-theme` from the stored mode before the page is painted, so there is no flash of the other mode. It is `headScript` in `src/lib/head-scripts.ts`, and `src/app/layout.tsx` inlines it.

The script changes `<html>` before React hydrates, that is, before React in the browser takes over the HTML Next.js built. React would see a difference there, so `<html>` has `suppressHydrationWarning`.

## useColorMode

Call the `useColorMode` hook to read and change the mode. It keeps state, so call it in a client component, a file that starts with `"use client"`. It returns the chosen mode and a setter. The setter stores the mode, sets `data-bs-theme` and fires `adminkit:colormodechange` on `document`:

```tsx
"use client";

import { useColorMode } from "@/hooks/use-config"; // free version: "@/hooks/use-color-mode"

export function DarkModeSwitch() {
  const [colorMode, setColorMode] = useColorMode();

  return (
    <button
      type="button"
      className="btn btn-light"
      onClick={() => setColorMode(colorMode === "dark" ? "light" : "dark")}
    >
      {colorMode === "dark" ? "Light mode" : "Dark mode"}
    </button>
  );
}
```

While Next.js renders the page, `colorMode` is `"auto"`. It becomes the stored mode once the page has hydrated in the browser.

## Toggle

In the [navbar](/nextjs/docs/navigation/navbar/), a dropdown offers light, dark and auto, and its button shows the icon of the current mode. It is `NavbarColorMode`, in `src/components/navbar/navbar-color-mode.tsx`. For an app with a single mode, remove it from `src/components/navbar/navbar.tsx`.

In AdminKit PRO, the [settings panel](/nextjs/docs/customization/theming/#settings-panel) has the same three modes, and demo links can preset one with `?colorMode=light`, `dark` or `auto`.

## Theme colors

Charts and maps draw with JavaScript, not CSS, so they read the colors of the current mode from the `useTheme` hook in `src/hooks/use-theme.ts`. The hook returns the colors from the CSS custom properties: `primary`, `secondary`, `success`, `info`, `warning`, `danger`, `gray-100` to `gray-900`, `white` (the surface color), `black` (the strongest text color), `body-bg`, `body-color`, `border-color`, and `id` (`"light"` or `"dark"`). Components that use it render again with the new colors when the mode changes.

The colors only exist in the browser. While the page renders on the server and hydrates, `useTheme` returns `null`.

The chart and map components call it for you: pass a function of the theme colors and they redraw when the mode changes. See [Charts](/nextjs/docs/charts/overview/).

## Your own styles

To make your own styles follow the mode, use the custom properties that change with it. Those are Bootstrap's `--bs-*` variables and the gray scale `--bs-gray-100` to `--bs-gray-900`, which runs the other way in dark mode. There is also `--ak-surface-bg`, the background of cards, dropdowns and inputs.

In Sass, the dark mode's values are the `$*-dark` variables in `src/scss/1-variables/_dark.scss`, based on `$dark-theme-base`. When a rule should only apply in dark mode, wrap it in Bootstrap's mixin:

```scss
.my-panel {
  background: var(--ak-surface-bg);

  @include color-mode(dark) {
    box-shadow: none;
  }
}
```

For a part that stays dark on a light page, put `data-bs-theme` on that element alone:

```tsx
<div className="card" data-bs-theme="dark">
  <div className="card-body">This card is dark in both modes.</div>
</div>
```

## Related

- [React Bootstrap dark mode](/react/docs/customization/dark-mode/): the same color modes in AdminKit for React (Vite)
- [Bootstrap 5 dark mode](/bootstrap/docs/getting-started/dark-mode/): dark mode in the HTML version
- [Theming](/nextjs/docs/customization/theming/): Sass variables and the sidebar colors
- [Dark mode demo](https://nextjs-demo.adminkit.io/?colorMode=dark)

---

Canonical HTML page: https://adminkit.io/nextjs/docs/customization/dark-mode/

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
