Next.js + TailwindCSS v4:如何使用 Next-Themes 添加暗/亮主题
Source: Dev.to
TailwindCSS v4 不再维护多个配置文件——所有内容现在都放在单个 global.css 文件中。这可能会让主题设置感觉有点挑战。在本指南中,我将展示如何使用 next‑themes 在你的 Next.js 项目中轻松设置亮色、暗色,甚至自定义主题。
第 01 步 – 初始化你的项目
👉 安装 Next.js 项目
pnpm create next-app my-project-name
pnpm install
pnpm dev
👉 安装 Next‑Themes
pnpm add next-themes
Source: …
第 02 步 – 修改 layout.tsx
👉 用 <ThemeProvider> 包裹应用(children)
下面是一个最小化的 layout.tsx,保留了所有默认的 Next.js 代码并添加了 provider。
import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css";
import { ThemeProvider } from "next-themes";
const geistSans = Geist({
variable: "--font-geist-sans",
subsets: ["latin"],
});
const geistMono = Geist_Mono({
variable: "--font-geist-mono",
subsets: ["latin"],
});
export const metadata: Metadata = {
title: "Create Next App",
description: "Generated by create next app",
};
export default function RootLayout({
children,
}: Readonly) {
return (
{children}
);
}
👉 为 ThemeProvider 添加属性
{children}
属性说明
| 属性 | 描述 |
|---|---|
enableSystem={true} | 允许应用遵循用户操作系统层面的配色方案偏好。(默认 false) |
defaultTheme="system" | 决定首次访问时加载的主题。使用 "system" 会遵循操作系统设置。 |
⚠️ Hydration 警告修复
如果看到警告 “A tree hydrated but some attributes of the server‑rendered HTML didn’t match the client properties”,请在 <ThemeProvider> 标签上添加 suppressHydrationWarning:
{children}
注意:
ThemeProvider是一个 客户端组件,而不是服务器组件。
第03步 – 自定义颜色
👉 global.css
添加 Tailwind 导入并为暗模式自定义变体:
@import 'tailwindcss';
/* Enable dark variant based on data-theme attribute */
@custom-variant dark (&:where([data-theme=dark], [data-theme=dark] *));
对于 Tailwind v4,需要
@import 'tailwindcss'。
@custom-variant行使 Tailwind 的dark:实用程序能够在[data-theme="dark"]属性下工作。
示例用法
Next Theme With TailwindCSS v4
当暗色主题激活时,将应用 bg-amber-500。
👉 定义主题变量
@theme {
/* Light mode background */
--bg-color-light-default: hsl(220, 14%, 96%);
/* Dark mode background */
--bg-color-dark-default: hsl(207, 95%, 8%);
}
👉 将变量应用到根元素
:root[data-theme="light"] {
background-color: var(--bg-color-light-default);
}
:root[data-theme="dark"] {
background-color: var(--bg-color-dark-default);
}
现在整个应用程序将根据激活的主题继承相应的背景颜色。
第 04 步 – 添加主题切换按钮
我们将使用 lucide‑react 图标库来实现切换按钮。
安装图标
pnpm install lucide-react
创建 ThemeTogglerBtn.tsx
'use client';
import { useTheme } } from "next-themes";
import { useEffect, useState } from "react";
import { Sun, Moon } from "lucide-react";
export default function ThemeTogglerBtn() {
const [mounted, setMounted] = useState(false);
const { theme, setTheme, resolvedTheme } = useTheme();
// Ensure the component only renders after hydration
useEffect(() => setMounted(true), []);
const toggleTheme = () => {
setTheme(resolvedTheme === "dark" ? "light" : "dark");
};
if (!mounted) {
return (
);
}
const currentIcon =
resolvedTheme === "dark" ? (
) : (
);
return (
{currentIcon}
{resolvedTheme === "dark" ? "Switch to light theme" : "Switch to dark theme"}
);
}
现在可以在 UI 任意位置(例如页眉)导入 ThemeTogglerBtn,让用户在浅色和深色模式之间切换。
🎉 完成!
你现在拥有:
- 使用 next‑themes 搭建的 Next.js 项目。
- 已配置 Tailwind v4,以响应
data-theme属性。 - 用于浅色/深色背景的自定义 CSS 变量。
- 可复用的客户端切换按钮。
随意为主题系统添加更多自定义调色板,甚至实现仅“系统”模式。祝编码愉快!
主题切换按钮
{currentIcon}
Theme switcher button
useTheme 钩子
import { useTheme } from 'next-themes';
const { theme, setTheme, resolvedTheme } = useTheme();
| 变量 | 描述 |
|---|---|
theme | 显示当前选中的主题('light' 或 'dark')。 |
setTheme | 用于更改主题的设置函数。 |
resolvedTheme | 检测系统首选主题(实际在用户设备上激活的主题)。 |
mounted 状态
在 Next.js 应用中使用 next-themes 时,主题值在服务器端渲染(SSR)和客户端水合(hydration)之间可能不一致。为避免出现不匹配,我们会跟踪一个 mounted 标记,该标记仅在组件在客户端挂载后才会变为 true。
import { useState, useEffect } from 'react';
const [mounted, setMounted] = useState(false);
useEffect(() => {
setMounted(true);
}, []);
- 初始状态:
mounted = false(SSR)。 - 客户端挂载后:
mounted = true。
这确保主题切换器仅在客户端运行。
Source:
主题切换函数
const toggleTheme = () => {
setTheme(resolvedTheme === 'dark' ? 'light' : 'dark');
};
- 如果
resolvedTheme为'dark',函数会切换为'light'。 - 如果
resolvedTheme为'light',函数会切换为'dark'。
图标选择
import { Sun, Moon } from 'react-feather';
const currentIcon = resolvedTheme === 'dark' ? (
) : (
);
- 暗模式: 渲染 Sun 图标。
- 亮模式: 渲染 Moon 图标。
按钮 UI
{currentIcon}
Theme switcher button
- 点击按钮会触发
toggleTheme。 {currentIcon}显示相应的图标。…为屏幕阅读器提供可访问的标签。
✅ 一目了然
- Install
next-themes。 - Wrap your app with
<ThemeProvider>。 - Configure
global.csswith the@custom-variant dark(or Tailwind’sdark:variant)。 - Add the theme toggler button using the
useThemehook。 - Enjoy smooth dark/light theme switching! 🎉