Skip to content

Instantly share code, notes, and snippets.

@darylwright
Created July 9, 2023 03:34
Show Gist options
  • Save darylwright/018a3e8996d8e2c215bf184fa68b0791 to your computer and use it in GitHub Desktop.
Save darylwright/018a3e8996d8e2c215bf184fa68b0791 to your computer and use it in GitHub Desktop.
This is an implementation of theming in a Next.js app using the App Router API.
import {ReactNode, ReactElement} from "react";
import RootComponent from "@/components/root";
export default function RootLayout({ children }: { children: ReactNode }): ReactElement {
return (
<html suppressHydrationWarning>
<body>
<RootComponent>
{children}
</RootComponent>
</body>
</html>
);
}
"use client";
import {ReactNode, ReactElement} from "react";
import {ThemeProvider} from "@wits/next-themes";
type RootProps = {
children: ReactNode;
}
export default function RootComponent({ children }: RootProps): ReactElement {
return (
<ThemeProvider attribute="class" themes={[`light`, `dark`]} enableSystem>
{children}
</ThemeProvider>
);
}
/* eslint-disable react/jsx-props-no-spreading */
import {ComponentPropsWithoutRef, ReactElement, useEffect, useState} from "react";
/*
TODO: See this GitHub issue: https://github.com/pacocoursey/next-themes/issues/161
The original next-themes package does not work with the App Router API in Next.js.
The package by user @wits enables this as indicated in the above issue. Use this
package until the original by @pacocoursey is updated to work with App Router.
*/
import {useTheme} from "@wits/next-themes";
type ThemeButtonProps = Omit<ComponentPropsWithoutRef<`button`>, `children`>;
export default function ThemeButton({ className, ...props }: ThemeButtonProps): ReactElement | null {
const [mounted, setMounted] = useState(false);
const { theme, setTheme } = useTheme();
useEffect(() => {
if (theme === `system`) {
const isSystemThemeDark = window.matchMedia(`(prefers-color-scheme: dark)`).matches;
setTheme(isSystemThemeDark ? `dark` : `light`);
}
setMounted(true);
}, []);
if (!mounted) {
return null;
}
return (
<button
type="button"
onClick={() => setTheme(theme === `dark` ? `light` : `dark`)}
{...props}
>
<span>
{theme === `dark` ? `🌙` : `☀️`}
</span>
</button>
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment