0.1.2 • Published 6 months ago

next-safe-themes v0.1.2

Weekly downloads
-
License
MIT
Repository
github
Last release
6 months ago

Next Safe Themes

Themes for Next.js, safe, no hydration errors.

Features

  • ✅ Any number of themes with a default one
  • ✅ Sync with multiple tabs
  • ✅ Supports Next.js app router
  • ✅ No flash on load
  • ✅ Server rendering
  • ✅ No hydration errors
  • ✅ Custom configurations
  • useTheme hook

Motivation

I can't find yet another theming library for Next.js besides next-themes. Next-themes has hydration errors. It encourage developers to put suppressHydrationWarning on the root HTML tag. And it encourage developers to flash the theme switch after page loaded. I want my website to render correctly. Thus, I created this library.

Installation

npm i next-safe-themes

Usage

Follow these steps, to theme your website without hydration errors.

1. Update Middleware

Update your src/middleware.ts like this.

import { createThemesMiddleware } from 'next-safe-themes/server/middleware'

const themesMiddleware = createThemesMiddleware(yourPreviousMiddleware)

export function middleware(request: NextRequest) {
  return themesMiddleware(request)
}

2. Update Layout

Update your layout file like this.

import { headers } from "next/headers"
import { getTheme } from "next-safe-themes/server/theme"
import { htmlThemeProps } from "next-safe-themes/props"
import { ThemeProvider } from "next-safe-themes/client/provider"

export default async function Layout({ children }) {

  // 1. get theme from middleware updated headers
  const theme = getTheme(await headers())

  // 2. render HTML attributes with the theme value
  return <html lang={locale} {...htmlThemeProps(theme)}>
    <head>
    ...
    </head>
    <body>
      <ThemeProvider initialTheme={theme}>
        {children}
      </ThemeProvider>
    </body>
  </html>
}

3. Theme Switch

Next-safe-themes provides a hook for you to write your custom theme switch.

const MyThemeSwitch = () => {
  const [theme, setTheme] = useTheme()
  return <div>
    <div>Current theme is: {theme}</div>
    <button onClick={() => setTheme('light')}>Light</button>
    <button onClick={() => setTheme('dark')}>Dark</button>
    <button onClick={() => setTheme('system')}>System</button>
  </div>
}

Configuration

You can customize the generated HTML attributes. Pass this configuration object as the second parameter to htmlThemeProps and ThemeProvider.

const themeConfig = {
  dataTheme: true, // data-theme="light"
  class: true, // class="light"
  additionalClassPrefix: "scheme-", // class="scheme-light"
  style: true, // style="color-scheme: light;"
}

Do not specify all 4, normally you may need 2 of them according to your CSS settings.

Pass the config to the functions like this.

export default async function Layout({ children }) {
  return <html lang={locale} {...htmlThemeProps(theme, themeConfig)}>
    <head>
    ...
    </head>
    <body>
      <ThemeProvider initialTheme={theme} themeConfig={themeConfig}>
        {children}
      </ThemeProvider>
    </body>
  </html>
}

License

MIT License.

0.1.2

6 months ago

0.1.1

6 months ago

0.1.0

6 months ago