AtroUIAtroUI
DocsComponentsBlogTheming
StarOwn the UI

Blog

ThemeProvider and dark mode with AtroUI and next-themes

2026-08-05

Wire dark mode for AtroUI registry components: class strategy, default dark, and tokens so dark-first styles actually apply.

AtroUI’s tokens assume a class-based dark theme. Whether you use next-themes or your own provider, .dark on the html element is what makes CSS variables resolve correctly.

Skip the provider, or use the wrong attribute, and you get a half-themed app: components render, but backgrounds and brand colors miss the dark sheet.

Install the peer if needed

If you add @atroui/theme-toggle or similar, the CLI may pull next-themes. Install it if your layout does not already have it.

bash
npm install next-themes

Recommended provider setup

Use attribute="class", defaultTheme="dark", and enableSystem if you want OS preference to win when the user has not chosen. suppressHydrationWarning on <html> avoids the classic theme flash warning.

tsx
import { ThemeProvider } from "next-themes"

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en" className="dark" suppressHydrationWarning>
      <body className="min-h-screen bg-background text-foreground antialiased">
        <ThemeProvider attribute="class" defaultTheme="dark" enableSystem>
          {children}
        </ThemeProvider>
      </body>
    </html>
  )
}

Fonts and the dark class

Load Outfit with variable: "--font-outfit" and put that variable on <html> so display styles match the catalog. You can keep className="dark" on html for first paint while ThemeProvider manages the class afterward.

Full layout snippet: Install AtroUI in Next.js.

Tokens still come from globals

ThemeProvider toggles the class. atroui/globals.css defines what .dark means. Import globals once. Override --brand and neutrals in your CSS when you re-skin. See dark-first tokens and Theming.

Common mistakes

Forgetting next-themes in package.json.

Using data-theme when tokens expect .dark.

Importing globals in a leaf component instead of the root layout.

Expecting light-first defaults. AtroUI is dark-first; light is the alternate.

Own the UIDocsAll posts