Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Prevent page flash in Next.js 12 with Tailwind CSS class-based dark mode

How can one prevent the page flash when using class-based dark mode in Tailwind CSS with Next.js v12 without using any 3rd party pkgs like next-themes?

I've looked at:

  • this Q&A How to fix dark mode background color flicker in NextJS? and while it's a valid/working solution in Next.js <v12, in v12 it no longer works and throws warnings in dev which suprisingly turn to a build blocking error in prod env Do not add <script> tags using next/head (see inline <script>). Use next/script instead. See more info here: https://nextjs.org/docs/messages/no-script-tags-in-head-component
  • This article https://www.vidyasource.com/blog/dark-mode-nextjs-tailwindcss-react-hooks however including the script <Script strategy="beforeInteractive" src="/scripts/darkMode.js"/> still results in a page flash as it adds defer to it in the head
  • This official Tailwind CSS dark mode doc on what's required https://tailwindcss.com/docs/dark-mode#toggling-dark-mode-manually
// On page load or when changing themes, best to add inline in `head` to avoid FOUC
if (localStorage.theme === 'dark' || (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
    document.documentElement.classList.add('dark')
} else {
    document.documentElement.classList.remove('dark')
}

I think they restrict putting things in Head from v12 to prepare for Suspense / Streaming / React v18.

Either way, I'm lost on how to do it without next-themes, does anyone know how can we just inject that bit of script to prevent that page flash?

Hope this question makes sense, if not, please give me a shout.

I like simple and minimalistic things, hence the aim to reduce the dependency on 3rd party pkgs, such a simple thing should be possible without overcomplicated solution IMO.

like image 775
user2723025 Avatar asked Dec 03 '25 19:12

user2723025


1 Answers

I had the same problem and solved it like this in Next 12.1.0:

  1. Create theme.js within the public folder (mine has the following content):
;(function initTheme() {
  var theme = localStorage.getItem('theme') || 'light'
  if (theme === 'dark') {
    document.querySelector('html').classList.add('dark')
  }
})()
  1. Add <Script src="/theme.js" strategy="beforeInteractive" /> to _app.tsx or _app.jsx. This is important - I tried putting it inside _document.tsx which didn't work. The final _app.tsx looks like this:
import '../styles/globals.css'
import type { AppProps } from 'next/app'
import Head from 'next/head'
import Script from 'next/script'

function App({ Component, pageProps }: AppProps) {
  return <>
    <Head>
      <meta name="viewport" content="initial-scale=1.0, width=device-width" />
    </Head>
    <Script src="/theme.js" strategy="beforeInteractive" />
    <Component {...pageProps} />
  </>
}

export default App

Works like a charm for me - no flickering and all Next.js magic. :) Hope this works for you too.

like image 113
Ronald Blüthl Avatar answered Dec 05 '25 09:12

Ronald Blüthl



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!