🫠
Next.jsでライト・ダークモード切替を実装する
Next.jsでのライト・ダークモード切り替えについて沢山の記事を目にしました。その中でも
Dave Grayさんの動画がわかりやすかったのでこの記事でまとめてみました。
- ライト・ダークモードの手動切り替えボタンを実装します。
[https://youtu.be/7zqI4qMDdg8]
参考にした動画です、動画時間も約10分なので気軽に見れます
環境
Next.js 15.0.3
Tailwind CSS 3.4.1
実装
tailwind.config.tsの編集
tailwind.config.tsにdarkMode: ["class"],
という一文を追加します。この設定によりdark:
プレフィックスを使用したスタイリングが有効になります。
tailwind.config.ts
import type { Config } from "tailwindcss";
export default {
+ darkMode: ["class"],
content: [
"./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
"./src/components/**/*.{js,ts,jsx,tsx,mdx}",
"./src/app/**/*.{js,ts,jsx,tsx,mdx}",
],
theme: {
extend: {
colors: {
background: 'hsl(var(--background))',
foreground: 'hsl(var(--foreground))',
card: {
DEFAULT: 'hsl(var(--card))',
foreground: 'hsl(var(--card-foreground))'
},
popover: {
DEFAULT: 'hsl(var(--popover))',
foreground: 'hsl(var(--popover-foreground))'
},
primary: {
DEFAULT: 'hsl(var(--primary))',
foreground: 'hsl(var(--primary-foreground))'
},
secondary: {
DEFAULT: 'hsl(var(--secondary))',
foreground: 'hsl(var(--secondary-foreground))'
},
muted: {
DEFAULT: 'hsl(var(--muted))',
foreground: 'hsl(var(--muted-foreground))'
},
accent: {
DEFAULT: 'hsl(var(--accent))',
foreground: 'hsl(var(--accent-foreground))'
},
destructive: {
DEFAULT: 'hsl(var(--destructive))',
foreground: 'hsl(var(--destructive-foreground))'
},
border: 'hsl(var(--border))',
input: 'hsl(var(--input))',
ring: 'hsl(var(--ring))',
chart: {
'1': 'hsl(var(--chart-1))',
'2': 'hsl(var(--chart-2))',
'3': 'hsl(var(--chart-3))',
'4': 'hsl(var(--chart-4))',
'5': 'hsl(var(--chart-5))'
}
},
borderRadius: {
lg: 'var(--radius)',
md: 'calc(var(--radius) - 2px)',
sm: 'calc(var(--radius) - 4px)'
}
}
},
plugins: [require("tailwindcss-animate")],
} satisfies Config;
next-themesをインストール
bash
npm install next-themes
ThemeProviderを設定
src/app/providers.tsx
というファイルを作成します。
providers.tsx
'use client'
import { ThemeProvider } from 'next-themes'
export function Providers({ children }: { children: React.ReactNode }) {
return <ThemeProvider attribute="class">{children}</ThemeProvider>
}
このProvidersをsrc/app/layout.tsxで使用します
layout.tsx
import { Providers } from "./providers";
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="ja" suppressHydrationWarning>
<body
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
suppressHydrationWarning
> // 👇 このProvidersを追加
<Providers>{children}</Providers>
</body>
</html>
);
}
テーマ切り替えボタンを作成
※今回 Myna ui iconsの Lightning, LightningSlashを使いました!
darkModeSwitch.tsx
"use client";
import { Lightning, LightningSlash } from "@mynaui/icons-react";
import { useState, useEffect } from "react";
import { useTheme } from "next-themes";
export default function Switch() {
const [mounted, setMounted] = useState(false);
const { setTheme, resolvedTheme } = useTheme();
useEffect(() => setMounted(true), []);
if (!mounted) {
return null;
}
if (resolvedTheme === "dark") {
return <LightningSlash size={30} onClick={() => setTheme("light")} />;
}
if (resolvedTheme === "light") {
return <Lightning size={30} onClick={() => setTheme("dark")} />;
}
}
ボタン配置
先ほど作成した切り替えボタンを好きな場所に配置して完成です。
私はheaderの右部に配置しました。
header.tsx
// 👇
import Switch from './darkModeSwitch'
export default function Header() {
const navItems = [
{ name: 'Blog', href: 'blog' },
{ name: 'About', href: 'about' },
{ name: 'Contact', href: 'about#contact' },
]
return (
<header className="sticky top-0 z-50 w-full border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
//(中略)
<Sheet>
<SheetTrigger asChild>
<Button
variant="ghost"
className="mr-2 px-0 text-base hover:bg-transparent focus-visible:bg-transparent focus-visible:ring-0 focus-visible:ring-offset-0 md:hidden"
>
<Menu className="h-5 w-5" />
<span className="sr-only">Toggle Menu</span>
</Button>
</SheetTrigger>
<SheetContent side="left" className="pr-0">
<nav className="flex flex-col gap-4">
{navItems.map((item, index) => (
<Link
key={index}
href={item.href}
className="block px-2 py-1 text-lg"
>
{item.name}
</Link>
))}
</nav>
</SheetContent>
// 👇
<Switch />
</Sheet>
</div>
</header>
)
}
ヘッダーの右部にライトモード時はLightningが、ダークモード時はLightningSlashが表示されます
おわりに
動画をみながらの実装になりましたが今回でNext.jsのライト・ダークモード切替を学習する事ができました。今後何か作る際は必ずライト・ダークモード切替をデフォルトで実装していきます!
Discussion