Next.js + Tailwind CSSで、 ちらつかないダークモードを実装する

これはなに Link to this heading

Next.js 14のApp Routerでダークモードを実装したときのメモ。 Tailwind CSSを利用。アプリはSSRでレンダリングする。

下記のサイトをほぼそのまま実装した。

App Directory dark mode with tailwindcss and SSR · vercel/next.js · Discussion #47952's image

App Directory dark mode with tailwindcss and SSR · vercel/next.js · Discussion #47952

Dark mode in app dir with tailwindcss and SSR Get theme from cookies in main layout // app/layout.tsx export default function RootLayout({ children, }: { children: React.ReactNode }) { const theme ...

github.com

LocalStorageを利用すると、ページをリロードしたときにちらついてしまう。そのため、Cookieを利用する。

手順 Link to this heading

taiwind.config.jsの設定を変更する Link to this heading

tailwind.config.jsdarkMode: 'class'を追加する。

tailwind.config.js
const config: Config = {
  // ...
+ darkMode: 'class',
};

cookieを取得する Link to this heading

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. テーマを定義する Link to this heading

typeとしてテーマを定義する。

app/_types/theme.ts
const Theme = {
  Light: 'light',
  Dark: 'dark',
} as const;

type Theme = (typeof Theme)[keyof typeof Theme];

export default Theme;

4. テーマ切り替えボタンを実装する Link to this heading

テーマを切り替えるためのボタンを実装する。

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. テーマ切り替えボタンを表示する Link to this heading

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. テーマを切り替える Link to this heading

テーマ切り替えボタンをクリックして、テーマが切り替わることを確認する。

参考文献・URL Link to this heading

App Directory dark mode with tailwindcss and SSR · vercel/next.js · Discussion #47952's image

App Directory dark mode with tailwindcss and SSR · vercel/next.js · Discussion #47952

Dark mode in app dir with tailwindcss and SSR Get theme from cookies in main layout // app/layout.tsx export default function RootLayout({ children, }: { children: React.ReactNode }) { const theme ...

github.com
Licensed under CC BY-NC-SA 4.0
最終更新 1月 20, 2024
Hugo で構築されています。
テーマ StackJimmy によって設計されています。