Back to all articles
Journal React
Sep 15, 2025 4 min read

How to Build a Theme Switcher with Mantine UI and React

Build a robust theme switcher in Mantine UI and React with custom color schemes, accessibility considerations, and scalable theming patterns.

MantineUI theme switcher

Introduction

Dark mode has become a standard feature in modern web apps, but true theming goes far beyond toggling black and white backgrounds. Enterprises increasingly need multiple accessible color schemes that align with brand identity, enhance readability, and adapt to user preferences.

MantineUI, a popular React component library, offers a powerful theme system that can be extended and customized. With it, you can implement advanced theme switching that supports not only light/dark modes but also custom palettes—while maintaining accessibility compliance.

In this article, we’ll explore:

  • How Mantine’s theme system works
  • Building a theme switcher component in React
  • Extending Mantine with custom color schemes
  • Ensuring accessibility when adding new themes

By the end, you’ll have a production-ready theme switcher that elevates user experience and maintains design consistency.


1. Understanding Mantine’s theme system

Mantine provides a <MantineProvider> component that accepts a theme object. This object defines:

  • Colors: Palettes defined as arrays of shades ([50...900])
  • Primary color: The main brand accent
  • Typography: Font families, sizes, weights
  • Other tokens: Radius, spacing, shadows

Basic usage:

import { MantineProvider } from "@mantine/core";

function App() {
  return (
    <MantineProvider theme={{ colorScheme: "light" }}>
      <MyApp />
    </MantineProvider>
  );
}

Mantine supports light and dark out of the box, but we can extend this to support custom themes.

2. Building a basic theme switcher

A simple theme switcher toggles between light and dark. Mantine provides a useMantineColorScheme hook.

import { ActionIcon, useMantineColorScheme } from "@mantine/core";
import { Sun, MoonStars } from "tabler-icons-react";

export function ThemeToggle() {
  const { colorScheme, toggleColorScheme } = useMantineColorScheme();
  const dark = colorScheme === "dark";

  return (
    <ActionIcon
      variant="outline"
      color={dark ? "yellow" : "blue"}
      onClick={() => toggleColorScheme()}
      title="Toggle color scheme"
    >
      {dark ? <Sun size={16} /> : <MoonStars size={16} />}
    </ActionIcon>
  );
}

This provides a light/dark switcher—but let’s go beyond.

3. Extending Mantine with custom color schemes

To add more themes (e.g., “Corporate”, “High Contrast”), define custom color palettes.

import { MantineProvider } from "@mantine/core";

const customTheme = {
  colorScheme: "light",
  colors: {
    corporateBlue: [
      "#e6f0ff",
      "#b3d1ff",
      "#80b3ff",
      "#4d94ff",
      "#1a75ff",
      "#005ce6",
      "#0047b3",
      "#003280",
      "#001c4d",
      "#00091a",
    ],
    highContrast: ["#000000", "#ffffff"], // 2-value contrast scheme
  },
  primaryColor: "corporateBlue",
};

function App() {
  return (
    <MantineProvider theme={customTheme}>
      <MyApp />
    </MantineProvider>
  );
}

Now you can switch palettes dynamically. Example of a dropdown-based switcher:

import { Select } from "@mantine/core";
import { useState } from "react";

export function ThemeSelector({ setTheme }) {
  const [value, setValue] = useState("light");

  return (
    <Select
      label="Choose theme"
      placeholder="Pick one"
      value={value}
      onChange={(val) => {
        setValue(val);
        setTheme(val);
      }}
      data={["light", "dark", "corporateBlue", "highContrast"]}
    />
  );
}

4. Accessibility considerations

Adding custom themes can introduce accessibility risks. Common pitfalls:

  1. Poor contrast
  • Problem: Brand colors often fail WCAG contrast standards.
  • Solution: Test palettes using WebAIM Contrast Checker.
  1. Semantic confusion
  • Problem: Some palettes use color alone to indicate state (e.g., errors).
  • Solution: Use icons, labels, or patterns in addition to color.
  1. User preference conflicts
  • Problem: Users may have OS-level preferences (prefers-color-scheme).
  • Solution: Respect system defaults but allow overrides.

Implementation tip:

const { colorScheme, setColorScheme } = useMantineColorScheme();

// Auto-detect system preference on load
useEffect(() => {
  const prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
  setColorScheme(prefersDark ? "dark" : "light");
}, []);

5. Pitfalls & best practices

  • Pitfall: Too many theme options → user confusion.
    • Best practice: Limit to 3–4 curated options.
  • Pitfall: Inconsistent branding across themes.
    • Best practice: Base all palettes on your design system tokens.
  • Pitfall: Theme logic scattered across app.
    • Best practice: Centralize theme management in a context or provider.

Conclusion

Dark mode alone is no longer enough. By extending MantineUI’s theme system, you can deliver multiple accessible and branded themes that improve user experience and inclusivity.

Key takeaways:

  • Mantine provides a strong base for theming via MantineProvider and hooks.
  • Advanced theme switching allows custom palettes beyond light/dark.
  • Accessibility must remain a priority—test contrast, add semantic cues, and respect user preferences.

👉 Want to integrate advanced theming systems into your React applications? Explore our UI/UX services here.

References & tools

Filed under

React Mantine UI Theming Dark mode Accessibility Component libraries

Keep reading

More articles you might want next

Continue with related writing on Astro, CSS architecture, accessibility, and modern UI engineering.

Strategy-led delivery

Ready to launch something revenue-focused?

If you need a premium website, conversion-focused landing page, or high-performance product interface shipped with speed, we can help you define the right scope, timeline, and next move.