next-themes两行代码就能解决主题切换!

next-themes是在next.js应用中解决主题色切换的方案,目前在 github 已经有4.4k star了。

它的用法也很简单,毕竟他的口号就是两行代码完美解决暗色模式,那么现在我们就来看看吧

本文是在 @next14 的环境,即 app router

如果是 page router,请去官网自助食用

用法

首先安装依赖

bash 复制代码
$ npm install next-themes
# or
$ yarn add next-themes

然后在layout中使用,这里suppressHydrationWarning其实就是帮我们处理添加data-theme,以方便我们切换主题

jsx 复制代码
// app/layout.jsx
import { ThemeProvider } from 'next-themes'

export default function Layout({ children }) {
  return (
    <html suppressHydrationWarning>
      <head />
      <body>
        <ThemeProvider>{children}</ThemeProvider>
      </body>
    </html>
  )
}

// 如果你是 tailwind,那么需要添加 attribute="class" 属性
// 具体参考 https://github.com/pacocoursey/next-themes?tab=readme-ov-file#with-tailwind

接着,我们通过api setTheme切换主题;不难发现,它是通过useTheme()解构出来的,使用很方便

jsx 复制代码
import { useTheme } from 'next-themes'

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

  return (
    <div>
      当前主题是: {theme}
      <button onClick={() => setTheme('light')}>亮色模式</button>
      <button onClick={() => setTheme('dark')}>暗色模式</button>
    </div>
  )
}

需要注意的是,当我们setTheme时,将会改变全局的主题色,也就是说其他页面的主题也会被我们影响到,因为上面我们说过,它的原理其实还是在html标签上修改data-theme。那么如果我们希望某个独特的页面固定为暗色主题,该怎么办呢?

下面就要开始介绍ThemeProvider的属性了,这里仅挑选重要的几个属性来讲~

  • storageKey = 'theme': 在 storage 里存储的 key,也就是说当我们 setTheme 时,也会在 localStoreage 里存储

  • forcedTheme: 上面的问题正需要这个属性了,这个属性是当前页面的强制主题色,使用方式如下

    jsx 复制代码
    function Providers({ children }) {
        const isForceDark = '根据路径或者其他方式判断'
        return(
         <ThemeProvider
          attribute="class"
          themes={['light', 'dark']}
          forcedTheme={isForceDark ? 'dark' : null}
         >
           {children}
         </ThemeProvider>
        )
    }
  • enableSystem = true: 是否跟随系统设置切换亮暗色

Hydration Error

在过去的一个月里,我的Next应用遇到这样一个问题:本地开发没有报错,但部署上线后却报了 10 个错误,打开一看全是hydration相关的

我们知道,hydration报错本质上就是ssrcsr不匹配,但线上报错本地不报错,着实让我摸不着头脑。这时组长给我明确了一个方向就是:ssr 的数据异常

还别说,打开network查看服务端渲染返回的HTML,还真找到了我的代码纰漏。不过修复后却依然存在这个报错,这是为什么呢?

原来就是这个next-themes库导致的问题,因为当 ssr 的时候其实拿不到theme此时为空,所以需要在 ssr 的时候返回 null,等到 csr 的时候再返回真正的UI

jsx 复制代码
import { useState, useEffect } from 'react'
import { useTheme } from 'next-themes'

const ThemeSwitch = () => {
  const [mounted, setMounted] = useState(false)
  const { theme, setTheme } = useTheme()

  // useEffect only runs on the client, so now we can safely show the UI
  useEffect(() => {
    setMounted(true)
  }, [])

  if (!mounted) {
    return null
  }

  return (
    <select value={theme} onChange={e => setTheme(e.target.value)}>
      <option value="system">System</option>
      <option value="dark">Dark</option>
      <option value="light">Light</option>
    </select>
  )
}

export default ThemeSwitch
相关推荐
崔庆才丨静觅5 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60616 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了6 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅6 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅7 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅7 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment7 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅7 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊7 小时前
jwt介绍
前端
爱敲代码的小鱼8 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax