Ashwin07
Ashwin07

Reputation: 3

Dark mode flashes light theme on page reload or refresh

import { Providers } from '@/components/Provider';
import { Head, Html, Main, NextScript } from 'next/document';

export default function Document() {
  return (
    <Html lang="en">
      <Head>
        <link rel="preconnect" href="https://fonts.googleapis.com" />
        <link rel="preconnect" href="https://fonts.gstatic.com" />
        <link
          href="https://fonts.googleapis.com/css2?family=Manrope:wght@200;300;400;500;600&display=swap"
          rel="stylesheet"
        />
        <link
          rel="apple-touch-icon"
          sizes="180x180"
          href="/apple_touch_icon.png"
        />
        <link
          rel="icon"
          type="image/png"
          sizes="32x32"
          href="/favicon_32x32.png"
        />
        <link
          rel="icon"
          type="image/png"
          sizes="16x16"
          href="/favicon_16x16.png"
        />
        <link rel="manifest" href="/site.webmanifest" />
        {/* Google Tag Manager */}
        <script
          dangerouslySetInnerHTML={{
            __html:
              `(function(w,l){` +
              `w[l] = w[l] || [];w[l].push({'gtm.start':new Date().getTime(),event:'gtm.js'});` +
              `})(window,'dataLayer');`,
          }}
        />
        {/* eslint-disable-next-line @next/next/no-sync-scripts */}
        <script src="/__ENV.js" />
      </Head>
      <body className="overflow-x-hidden dark:bg-black-300">
        <Providers>
          <Main />
          <NextScript />
        </Providers>
      </body>
    </Html>
  );
}

//_app.tsx
import '@/styles/globals.css';
import type { AppProps } from 'next/app';
import dynamic from 'next/dynamic';
import { ThemeProvider } from 'next-themes';
import '../../public/common.css';
import type { NextPageWithLayout } from '@/utils/types';
import Script from 'next/script';
import { env } from 'next-runtime-env';

type AppPropsWithLayout = AppProps & {
  Component: NextPageWithLayout;
};

export default function App({ Component, pageProps }: AppPropsWithLayout) {
  const getLayout = Component.getLayout ?? ((page) => page);

  return (
    <>
      <Script
        src={`https://www.googletagmanager.com/gtm.js?id=${env(
          'NEXT_PUBLIC_GTM_ID',
        )}`}
      />

      <ThemeProvider attribute="class">
        {getLayout(<Component {...pageProps} />)}
      </ThemeProvider>
    </>
  );
}

`The website is developed using Next.js and Tailwind CSS for styling, with theme switching implemented via next-themes. While the issue isn't apparent locally, in production, a brief flash of the light theme occurs, suggesting a delay, possibly due to slow network speeds or server-side rendering in Next.js.

Upon reloading the page in production, both the "dark" class and the inline style "color-scheme: dark;" are momentarily removed from the tag. However, after inspecting similar websites that also utilize Next.js and next-themes, I noticed they don't have this flickering issue. Instead, only the "dark" class is removed and re-added upon page reload, while the "color-scheme: dark;" style remains consistent. My website behaves the same way in local development, but in production, both of the classes get removed, which might be causing the issue.`

Upvotes: 0

Views: 395

Answers (1)

djohoe28
djohoe28

Reputation: 45

Unfortunately, at time of writing this is an open issue on Material UI's end -- some snooping around implies

  1. They're still working on support for both Next.js (probably for the modern App Router, since they do have documentation for the old Page Router) and React 19 (which is currently experimental, so not necessarily relevant in your case).
  2. There are a couple workarounds;
    • Just use the "use client" directive on essentially everything, negating Next.js/React optimization features for Server-Side Components
    • Hard-code data-mui-color-scheme="dark" on whichever part you'd like to force into dark-mode (so if you do find out the client wants light-mode, it'd be less jarring to switch into than light-to-dark).

I've been looking into this quite a bit over the past 24 hours, and this is the best I've got so far 😕

EDIT: I forgot to mention this article, which has some example of using React Context for custom themes on the server-side, but honestly I'm not sure why it works (if it still does 😅)

EDIT 2: Look into this maybe..?

Upvotes: 1

Related Questions