Bootstrap 5.3 added something I'd wanted for a long time — native dark mode support. No more maintaining two separate stylesheets. No more toggling classes on hundreds of elements. Just set data-bs-theme="dark" on your <html> tag and Bootstrap handles the rest.

Here's how to do it properly, including the toggle, system preference detection and localStorage persistence.

How Bootstrap 5 Dark Mode Works

In Bootstrap 5.3+, every component reads from CSS variables that change based on the data-bs-theme attribute. Light is the default. Set data-bs-theme="dark" on any element and all Bootstrap components inside it switch to dark mode automatically.

That's it at the CSS level. No extra stylesheet, no class switching on every element.

Basic Setup — Site-Wide Dark Mode

<!-- Light mode (default) -->
<html lang="en">

<!-- Dark mode -->
<html lang="en" data-bs-theme="dark">

Every Bootstrap component inside responds: navbar goes dark, cards get dark backgrounds, modals, dropdowns, tables — all of it. One attribute.

Adding a Toggle Switch

Here's the JavaScript to toggle between modes:

// Get current theme
function getTheme() {
  return localStorage.getItem('theme') || 'light'
}

// Set theme
function setTheme(theme) {
  document.documentElement.setAttribute('data-bs-theme', theme)
  localStorage.setItem('theme', theme)
  updateToggle(theme)
}

// Update toggle button appearance
function updateToggle(theme) {
  const btn = document.getElementById('themeToggle')
  if (!btn) return
  btn.textContent = theme === 'dark' ? '☀️ Light Mode' : '🌙 Dark Mode'
}

// Apply saved theme on page load
setTheme(getTheme())

And the HTML toggle button:

<button id="themeToggle" class="btn btn-outline-secondary btn-sm"
  onclick="setTheme(getTheme() === 'dark' ? 'light' : 'dark')">
  🌙 Dark Mode
</button>

Detecting System Preference

If the user hasn't set a preference yet, default to their system setting:

function getTheme() {
  // Check localStorage first
  const saved = localStorage.getItem('theme')
  if (saved) return saved

  // Fall back to system preference
  return window.matchMedia('(prefers-color-scheme: dark)').matches
    ? 'dark'
    : 'light'
}

Also listen for system preference changes while the page is open:

window.matchMedia('(prefers-color-scheme: dark)')
  .addEventListener('change', (e) => {
    // Only update if user hasn't manually set a preference
    if (!localStorage.getItem('theme')) {
      setTheme(e.matches ? 'dark' : 'light')
    }
  })

Component-Level Dark Mode

You can apply dark mode to specific sections without affecting the whole page:

<!-- Dark navbar on a light page -->
<nav class="navbar navbar-expand-lg" data-bs-theme="dark" style="background:#0d0d0d;">
  <!-- navbar content -->
</nav>

<!-- Dark sidebar -->
<aside data-bs-theme="dark" style="background:#1a1a2e;">
  <!-- sidebar content -->
</aside>

This is great for admin dashboards where you might want a dark sidebar but a light main content area.

Customizing Dark Mode Colors

Bootstrap's dark mode uses CSS variables you can override:

[data-bs-theme="dark"] {
  --bs-body-bg: #0a0a0a;           /* main background */
  --bs-body-color: #e8e8e8;         /* main text color */
  --bs-card-bg: #1a1a2e;            /* card background */
  --bs-border-color: #2d2d2d;       /* border color */
  --bs-secondary-bg: #111827;       /* secondary backgrounds */
}

Put this in your main CSS file after importing Bootstrap. These variables cascade down through every Bootstrap component.

Full Working Example

Here's a complete minimal dark mode page:

<!DOCTYPE html>
<html lang="en" id="htmlRoot">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Bootstrap 5 Dark Mode</title>
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
  <style>
    [data-bs-theme="dark"] {
      --bs-body-bg: #0a0a0a;
      --bs-card-bg: #1a1a2e;
    }
  </style>
</head>
<body>
  <nav class="navbar navbar-expand-lg bg-body-tertiary">
    <div class="container">
      <a class="navbar-brand fw-bold" href="#">My App</a>
      <button id="themeToggle" class="btn btn-outline-secondary btn-sm ms-auto">
        🌙 Dark Mode
      </button>
    </div>
  </nav>

  <div class="container py-5">
    <div class="card">
      <div class="card-body">
        <h5 class="card-title">Dark Mode Works</h5>
        <p class="card-text text-muted">This card adapts to light and dark mode automatically.</p>
        <button class="btn btn-primary">Primary Button</button>
      </div>
    </div>
  </div>

  <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
  <script>
    const root = document.getElementById('htmlRoot')
    const btn = document.getElementById('themeToggle')

    function getTheme() {
      return localStorage.getItem('theme') ||
        (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light')
    }

    function setTheme(theme) {
      root.setAttribute('data-bs-theme', theme)
      localStorage.setItem('theme', theme)
      btn.textContent = theme === 'dark' ? '☀️ Light Mode' : '🌙 Dark Mode'
    }

    btn.addEventListener('click', () => {
      setTheme(getTheme() === 'dark' ? 'light' : 'dark')
    })

    setTheme(getTheme())
  </script>
</body>
</html>

What Doesn't Auto-Switch

One thing to watch: custom background colors set with inline style="" or your own CSS classes won't switch automatically. Only Bootstrap's CSS variables respond to data-bs-theme.

If you've hardcoded style="background:#fff" on elements, those won't go dark. Use Bootstrap's utility classes like bg-body, bg-body-secondary instead — those use CSS variables and respond correctly.

Angular and React

For Angular, store the theme in a service and update the attribute on the document element:

setTheme(theme: 'light' | 'dark') {
  document.documentElement.setAttribute('data-bs-theme', theme)
  localStorage.setItem('theme', theme)
}

For React with Next.js, use next-themes — it handles Bootstrap's data-bs-theme attribute directly when configured with attribute="data-bs-theme".

Frequently Asked Questions

Bootstrap 5.3 introduced native color mode support via the data-bs-theme attribute. Set data-bs-theme='dark' on any element and Bootstrap automatically applies dark colors to all Bootstrap components inside it. Set it on the html element to apply site-wide.
The CSS works without JavaScript — just add data-bs-theme='dark' to your HTML element. You need JavaScript only for the toggle functionality and localStorage persistence.
Use the prefers-color-scheme media query in JavaScript: const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches. Set the theme accordingly on page load.
Yes. Add data-bs-theme='dark' to any element, not just html. The dark theme applies only to Bootstrap components inside that element. This lets you have a dark navbar or dark sidebar on an otherwise light page.
Override CSS variables in a [data-bs-theme='dark'] selector: [data-bs-theme='dark'] { --bs-body-bg: #0a0a0a; --bs-body-color: #e8e8e8; }. All Bootstrap components use these CSS variables so your overrides apply everywhere automatically.

Need a Full Bootstrap 5 Admin Dashboard?

Get a complete Angular 21 + Bootstrap 5 dashboard with 50+ components — built by the same team behind BootstrapPlanet.

Browse Templates →

Use code FIRST30 for 30% off your first purchase.

Related Components