Next.js + TailwindCSS v4:如何使用 Next-Themes 添加暗/亮主题

发布: (2025年12月20日 GMT+8 21:53)
6 min read
原文: Dev.to

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,让用户在浅色和深色模式之间切换。

🎉 完成!

你现在拥有:

  1. 使用 next‑themes 搭建的 Next.js 项目。
  2. 已配置 Tailwind v4,以响应 data-theme 属性。
  3. 用于浅色/深色背景的自定义 CSS 变量。
  4. 可复用的客户端切换按钮。

随意为主题系统添加更多自定义调色板,甚至实现仅“系统”模式。祝编码愉快!

主题切换按钮


  {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} 显示相应的图标。
  • 为屏幕阅读器提供可访问的标签。

✅ 一目了然

  1. Install next-themes
  2. Wrap your app with <ThemeProvider>
  3. Configure global.css with the @custom-variant dark (or Tailwind’s dark: variant)。
  4. Add the theme toggler button using the useTheme hook。
  5. Enjoy smooth dark/light theme switching! 🎉
Back to Blog

相关文章

阅读更多 »

你正在错误地使用 TailwindCSS

我之前已经提到过,为什么我通常不建议在我的项目中将 Tailwind CSS 作为主要的样式方案,并且我已经解释了这一立场……