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报错本质上就是ssr和csr不匹配,但线上报错本地不报错,着实让我摸不着头脑。这时组长给我明确了一个方向就是: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
相关推荐
沙蒿同学6 小时前
Wails v3 实战:一个桌面提醒客户端,从 v2 升到 v3 的全过程
前端·后端·github
inquisiter7 小时前
KernelGpt的部分尝试
java·前端·apache
the局外人7 小时前
用 TRAE 把 Markdown文档 搬到微信公众号
前端
Autumn_ing7 小时前
AI原型设计工具怎么选?可编辑原型与可运行前端适合什么场景
前端·小程序·产品经理·代码·codex·墨刀ai客户端·ai原型
夕阳_醉了7 小时前
前端通用组件开发全指南|从 0 到 1 打造稳定可复用的业务组件
前端
Amos_Web8 小时前
Rspack 源码解析(三十):SourceMapDevToolPlugin 如何生成并关联 source map
前端
console.log('npc')8 小时前
从0到1实现任务评价统计功能:React + TypeScript 实战指南(小白注释版)
前端·react.js·typescript
IT_陈寒8 小时前
为什么你应该学习React?
前端·人工智能·后端
ZoeLandia8 小时前
React04:事件处理与受控表单
开发语言·前端·react.js·typescript
亿元程序员8 小时前
用AI做了一款微信小游戏,却发现上传不了
前端