これはなに
Next.js 14のApp Routerでダークモードを実装したときのメモ。 Tailwind CSSを利用。アプリはSSRでレンダリングする。
下記のサイトをほぼそのまま実装した。
LocalStorageを利用すると、ページをリロードしたときにちらついてしまう。そのため、Cookieを利用する。
手順
taiwind.config.jsの設定を変更する
tailwind.config.js
にdarkMode: 'class'
を追加する。
tailwind.config.js
const config: Config = {
// ...
+ darkMode: 'class',
};
cookieを取得する
app/layout.tsx
でcookieからtheme
の値を取得し、html
タグのclassName
に設定する。
app/layout.tsx
import { cookies } from 'next/headers';
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
const theme = cookies().get('theme');
return (
<html lang="ja" className={theme?.value}>
{/* ... */}
</html>
);
}
3. テーマを定義する
type
としてテーマを定義する。
app/_types/theme.ts
const Theme = {
Light: 'light',
Dark: 'dark',
} as const;
type Theme = (typeof Theme)[keyof typeof Theme];
export default Theme;
4. テーマ切り替えボタンを実装する
テーマを切り替えるためのボタンを実装する。
app/_components/ThemeSwitch.tsx
'use client';
import { useState } from 'react';
import Theme from '@/app/_types/theme';
interface ThemeSwitchProps {
theme: Theme;
className?: string;
}
export default function ThemeSwitch(props: Readonly<ThemeSwitchProps>) {
const [theme, setTheme] = useState<Theme>(props.theme);
const toggleTheme = () => {
const root = document.getElementsByTagName('html')[0];
root.classList.toggle(Theme.Dark);
if (root.classList.contains(Theme.Dark)) {
setTheme(Theme.Dark);
document.cookie = `theme=${Theme.Dark}`;
} else {
setTheme(Theme.Light);
document.cookie = `theme=${Theme.Light}`;
}
};
return (
<button
className={['bg-gray-300 dark:bg-gray-700', props.className].join(' ')}
onClick={toggleTheme}
>
{theme == Theme.Light ? Theme.Light : Theme.Dark}
</button>
);
}
5. テーマ切り替えボタンを表示する
ThemeSwitch
を表示する。今回はapp/_components/MainHeader.tsx
で表示する例を示すが、任意の場所で構わない。
app/_components/MainHeader.tsx
import { cookies } from 'next/headers';
import ThemeSwitch from './ThemeSwitch';
import Theme from '@/app/_types/theme';
export default function MainHeader() {
const theme =
cookies().get('theme')?.value === Theme.Dark ? Theme.Dark : Theme.Light;
return (
<header>
<ThemeSwitch theme={theme} />
</header>
);
}
6. テーマを切り替える
テーマ切り替えボタンをクリックして、テーマが切り替わることを確認する。