next-themes icon indicating copy to clipboard operation
next-themes copied to clipboard

Add option to opt-out of automatic script insertion

Open plmrry opened this issue 1 year ago • 1 comments

Problem

We've seen a bug in Chrome where (in dark mode) there is a flash of light-themed code.

This appears to be due to how next-themes inserts <ThemeScript /> into the <body />

Solution

This PR:

  1. Adds an optional withScript prop to <Theme>.
  • This allows the user to optionally disable the insertion of <ThemeScript> by setting withScript to false.
  • withScript defaults to true, which preserves the default behavior of next-themes.
  1. Exports <ThemeScript>, so it can be manually inserted by the user (i.e. in the <head>)

  2. Adds an optional string id prop that gets added to the script itself

Example

This is pseudo-code:

import { ThemeProvider, ThemeScript } from 'next-themes'

export default function RootLayout({
  children
}): JSX.Element {
  return (
    <html>
      <head>
        <ThemeScript storageKey="my-theme" />
      </head>
      <body>
        <ThemeProvider storageKey="my-theme" withScript={false}>
        {children}
      </body>
    </html>
  )
}

Based on internal work from @cramforce:

This appears to fix a long-standing bug where you get a flash of light-theme in Chrome.

The primary change in the fork is to allow placing the inline script manually. With the default-usage of next-themes, the script always goes into the body (usually first).

plmrry avatar Apr 17 '25 18:04 plmrry

@styfle I removed that unnecessary id prop and added a unit test

plmrry avatar Apr 27 '25 15:04 plmrry