Published on

使用nextjs 实现暗黑模式

Authors
  • Name
    Azimuth Ti
    Twitter

Overview

使用 Next.js 社区提供的一个主题管理库 next-themes,可以轻松地实现暗黑模式切换,以下代码基于Nextjs15 + tailwindcss 4.x实现(3.x版本略有不同,查阅其他文档)

1、初始化项目

根据官方步骤初始化nextjs项目(使用typescript)

npx create-next-app@latest

2、进入项目目录安装依赖库

项目中用到了两个图标,依赖了lucide-react库

pnpm install next-themes lucide-react

3、修改默认的global.css文件


- @media (prefers-color-scheme: dark) {
-  :root {
-    --background: #0a0a0a;
-    --foreground: #ededed;
-  }
- }

+ @custom-variant dark (&:where(.dark, .dark *));

4、自定义ThemeProvider类

import { ThemeProvider as NextThemesProvider } from "next-themes";

export function ThemeProvider({ children }: { children: React.ReactNode }) {
  return (
    <NextThemesProvider
      attribute="class"
      defaultTheme={"light"}
      enableSystem
      disableTransitionOnChange
    >
      {children}
    </NextThemesProvider>
  );
}

5、修改app目录下面的layout.tsx

在layout.tsx中import上面自定义的ThemeProvider类,并做如下代码修改

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang="en">
      <body
        className={`${geistSans.variable} ${geistMono.variable} antialiased`}
      >
-       {children}
+       <ThemeProvider> {children}</ThemeProvider>
      </body>
    </html>
  );
}

6、自定义一个触发主题模式切换的组件

// /app/components/ThemeSwitch.tsx

'use client'

import { Moon, Sun } from 'lucide-react'
import { useTheme } from 'next-themes'

export default function ThemeSwitch() {
  const { theme, setTheme } = useTheme()

  return (
    <>
      <button
        onClick={() => {
          console.log('theme---->', theme)
          setTheme(theme == 'dark' ? 'light' : 'dark')
        }}
      >
        {theme == 'dark' ? (
          <Sun className="h-[1.2rem] w-[1.2rem] rotate-0 transition-all dark:fill-white dark:stroke-white" />
        ) : (
          <Moon className="h-[1.2rem] w-[1.2rem] fill-black transition-all" />
        )}
      </button>
    </>
  )
}

7、应用

在需要的位置引入主题模式切换的组件即可。

这里用默认的/app/page.tsx文件为例:

import ThemeSwitch from "@/components/ThemeSwitch";
import Image from "next/image";

export default function Home() {
  return (
    <div className="grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20 font-[family-name:var(--font-geist-sans)] dark:bg-black">
      <main className="flex flex-col gap-[32px] row-start-2 items-center sm:items-start dark:bg-black">
        <Image
          className="dark:invert"
          src="/next.svg"
          alt="Next.js logo"
          width={180}
          height={38}
          priority
        />
        <ThemeSwitch />
      </main>
    </div>
  );
}