2023.08.01

Next.js13でダークモード対応をする

このサイトをダークモード対応してみました🌞🌛

開発環境

  • Next.js 13.4.4
  • next-themes 0.2.1

手順

パッケージのインストール

next-themesというパッケージを使います。
https://www.npmjs.com/package/next-themes

$ npm install next-themes

プロバイダーの作成

next-themesのThemeProviderは、クライアントコンポーネントでしか動きません。
なので、新規ファイルproviders.tsxをappディレクトリ配下に作ります。

'use client';

import { ThemeProvider } from 'next-themes';

export function Providers({ children }: { children: React.ReactNode }) {
  return <ThemeProvider>{children}</ThemeProvider>;
}

ただ、このままではハイドレーションエラーが出てしまいます。
next-themesのGitHubにも以下の記述がありました。

Because we cannot know the theme on the server, many of the values returned from useTheme will be undefined until mounted on the client. This means if you try to render UI based on the current theme before mounting on the client, you will see a hydration mismatch error.

引用元:https://github.com/pacocoursey/next-themes#avoid-hydration-mismatch

つまり、サーバー側の情報とクライアント側の情報が一致していないよという状態ですね。
この対処法についてもGitHubに記載があったの、そちらを参考に以下のように対応しました。

'use client';

import { ReactNode, useEffect, useState } from 'react';
import { ThemeProvider } from 'next-themes';

export function Providers({ children }: { children: ReactNode }) {
  const [mounted, setMounted] = useState(false);

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

  if (!mounted) return <>{children}</>;

  return <ThemeProvider>{children}</ThemeProvider>;
}

ちなみに結構議論されていたみたいです。
https://github.com/pacocoursey/next-themes/issues/152

テーマスイッチャーの作成

実際にテーマを切り替えるためのコンポーネントを作成します。
next-themesのuseThemeというフックで簡単に切り替えを実装できます。

'use client';

import { useTheme } from 'next-themes';
import { LightModeIcon, DarkModeIcon } from '@/components/ui/icons/index';

export const ThemeChanger = () => {
  const { theme, setTheme } = useTheme();

  return (
    <div>
      {theme === 'light' ? (
        <button className="sun" onClick={() => setTheme('dark')}>
          <DarkModeIcon />
        </button>
      ) : (
        <button className="moon" onClick={() => setTheme('light')}>
          <LightModeIcon />
        </button>
      )}
    </div>
  );
};

ダークモード用のスタイルを書く

テーマの切り替えに応じて、htmlタグの属性が切り替わります。

ライトモードのとき

<html lang="ja" data-theme="light" style="color-scheme: light;">

ダークモードのとき

<html lang="ja" data-theme="dark" style="color-scheme: dark;">

この属性を使ってCSSを書けばOKです。

:root {
  --color-bg-main: #fff;
}

[data-theme='dark'] {
  --color-bg-main: #333;
}

body {
  background-color: var(--color-bg-main);
}

応用

Tailwind CSSと組み合わせて使う場合

Tailwind CSSの設定ファイルに追記します。

/** @type {import('tailwindcss').Config} */
module.exports = {
  // other settigns...
  darkMode: 'class',
};

これで、以下のようにdark:で記述した部分がダークモードのときに適応されるようになります💯

<p className="text-gray-500 dark:text-gray-100">text</p>

感想

わたし自身、ダークモードに切り替えたりすることはないのですが、最近は対応しているサイトも多いので実装してみました。

(7月中に2つ記事を投稿する予定が8月になってしまった🥲)

参考サイト

最後まで読んでいただきありがとうございます!
もしよければ「読んだよ!」の代わりに↑の紙飛行機をクリックで飛ばしてください。わたしの元に届きます。

Special Thanks!!!

ありがとうございました