better-themes
Frameworks

Remix / React Router v7

Setup guide for Remix and React Router v7

Installation

npm install better-themes

Setup

Create or update your root layout

In app/root.tsx, wrap your app with ThemeProvider:

app/root.tsx
import {
  Links,
  Meta,
  Outlet,
  Scripts,
  ScrollRestoration,
} from "react-router"; // or "@remix-run/react"
import { ThemeProvider } from "better-themes";

export function Layout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en" suppressHydrationWarning>
      <head>
        <meta charSet="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <Meta />
        <Links />
      </head>
      <body>
        <ThemeProvider attribute="class" disableTransitionOnChange>
          {children}
        </ThemeProvider>
        <ScrollRestoration />
        <Scripts />
      </body>
    </html>
  );
}

export default function App() {
  return <Outlet />;
}

Add suppressHydrationWarning

Add suppressHydrationWarning to your <html> tag in app/root.tsx to prevent hydration warnings.

Create a theme switcher component

Create a component for theme switching:

app/components/theme-switcher.tsx
import { useTheme } from "better-themes";
import { useEffect, useState } from "react";

export function ThemeSwitcher() {
  const [mounted, setMounted] = useState(false);
  const { theme, setTheme } = useTheme();

  useEffect(() => {
    setMounted(true);
  }, []);

  if (!mounted) {
    return null;
  }

  return (
    <button onClick={() => setTheme(theme === "dark" ? "light" : "dark")}>
      Toggle theme
    </button>
  );
}

Tailwind CSS Configuration

If you're using Tailwind CSS, configure it for class-based dark mode:

tailwind.config.js
export default {
  darkMode: ["class"],
  // ... rest of config
};

Complete Example

app/root.tsx
import {
  Links,
  Meta,
  Outlet,
  Scripts,
  ScrollRestoration,
} from "react-router";
import { ThemeProvider } from "better-themes";
import { ThemeSwitcher } from "./components/theme-switcher";

export function Layout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en" suppressHydrationWarning>
      <head>
        <meta charSet="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <Meta />
        <Links />
      </head>
      <body>
        <ThemeProvider attribute="class" disableTransitionOnChange>
          <ThemeSwitcher />
          {children}
        </ThemeProvider>
        <ScrollRestoration />
        <Scripts />
      </body>
    </html>
  );
}

Example

View a complete example implementation: Remix Example

Try the live demo: Live Demo

On this page