How to stop page theme FLASH in Next.JS 15 using Tailwind CSS and prevent hydration mismatch

Is it possible to implement a successful page theme initialisation without the common flash issue, and whilst also avoiding any hydration mismatch errors?

My solution so far has been to execute JavaScript before the full page renders, by using <script> in the <head> tag, to check the users localStorage, and then apply the theme.

This works perfectly, except I am getting a hydration error in the console. This is obviously because the localStorage can only be checked on the client side, so the server-rendered HTML and client-rendered HTML never match.

I’d just ignore this console error, but isn’t it true that a hydration mismatch indicates that React had to discard the server-rendered HTML and re-render the tree on the client? I’m not particularly happy with having to accept that for concern of performance degradation.

The only other solution there seems to be from my research is either to pass the users selected theme through cookies, which I’d rather less prefer as localStorage is more persistent – or to save the users preference server side, which seems a bit unnecessary all for the sake of avoiding a half second flash.

Is there really no way to do this using localStorage? My main question is more focused on avoiding this hydration issue. I’ve already got a solution that works, but I don’t want to avoid the hydration mismatch. The best I can come up with is setting a default Tailwind theme class such as light to the <html> tag. This way, I can eliminate half the issue by assuring the hydration error only occurs if the theme is dark, rather than there always being a mismatch. Would like to get the other half, but can’t logically think how to do it. Maybe there is some clever way?

Code below:

import type { Metadata } from "next";
import "./globals.css";
import NavBar from "./components/NavBar/NavBar";
import Footer from "./components/Footer/Footer";

export const metadata: Metadata = {
  title: "Create Next App",
  description: "Generated by create next app",
};

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  const initTheme = `
  (function() {
    const theme = localStorage.getItem("theme") || "light";
    document.documentElement.classList.remove('light', 'dark');
    document.documentElement.classList.add(theme);})();
  `;
  return (
    <html lang="en" className="light">
      <head>
        <link rel="stylesheet" href="https://rsms.me/inter/inter.css" />
        <script dangerouslySetInnerHTML={{ __html: initTheme }} />
      </head>
      <body>
        <NavBar></NavBar>
        {children}
        <Footer></Footer>
      </body>
    </html>
  );
}