Nextjs version 13 encountered hydration failure due to discrepancies between the initial UI and the server-rendered content

I am currently utilizing the latest version 13.1.0. I have implemented a ContextProvider that allows switching between light and dark themes.

'use client';
import { Theme, ThemeContext } from '@store/theme';
import { ReactNode, useState, useEffect } from 'react';

interface ContextProviderProps {
  children: ReactNode
}

const ContextProvider = ({ children }: ContextProviderProps) => {
  const [theme, setTheme] = useState<Theme>('dark');

  useEffect(() => {
    const storedTheme = localStorage.getItem('theme');
    if (storedTheme === 'light' || storedTheme === 'dark') {
      setTheme(storedTheme);
    } else {
      localStorage.setItem('theme', theme);
    }
    // added to body because of overscroll-behavior
    document.body.classList.add(theme);
    return () => {
      document.body.classList.remove(theme);
    };
  }, [theme]);

  const toggle = () => {
    const newTheme = theme === 'light' ? 'dark' : 'light';
    setTheme(newTheme);
    localStorage.setItem('theme', newTheme);
  };

  return (
    <ThemeContext.Provider value={{ theme, toggle }}>
      {children}
    </ThemeContext.Provider>
  );
};

export { ContextProvider };

This is integrated into my root layout

import '@styles/globals.scss';
import { GlobalContent } from '@components/GlobalContent/GlobalContent';
import { ContextProvider } from '@components/ContextProvider/ContextProvider';
import { Inter } from '@next/font/google';
import { ReactNode } from 'react';

const inter = Inter({ subsets: ['latin'] });

interface RootLayoutProps {
  children: ReactNode
}

const RootLayout = ({ children }: RootLayoutProps) => {
  return (
    <html lang="en" className={inter.className}>
      <head />
      <body>
        <ContextProvider>
          <GlobalContent>
            {children}
          </GlobalContent>
        </ContextProvider>
      </body>
    </html>
  );
};

export default RootLayout;

The theme value is consumed in the GlobalContent component

'use client';
import styles from '@components/GlobalContent/GlobalContent.module.scss';
import { GlobalHeader } from '@components/GlobalHeader/GlobalHeader';
import { GlobalFooter } from '@components/GlobalFooter/GlobalFooter';
import { ThemeContext } from '@store/theme';
import { ReactNode, useContext } from 'react';

interface GlobalContentProps {
  children: ReactNode
}

const GlobalContent = ({ children }: GlobalContentProps) => {
  const { theme } = useContext(ThemeContext);
  return (
    <div className={`${theme === 'light' ? styles.lightTheme : styles.darkTheme}`}>
      <GlobalHeader />
      <div className={styles.globalWrapper}>
        <main className={styles.childrenWrapper}>
          {children}
        </main>
        <GlobalFooter />
      </div>
    </div>
  );
};

export { GlobalContent };

An error has occurred:

Hydration failed because the initial UI does not match what was rendered on the server.

https://i.stack.imgur.com/oJb9b.png

React docs error link

I am puzzled by this error since I am using localStorage within my useEffect, so I anticipate the HTML generated on the server to be consistent with the client prior to the initial render.

How can I troubleshoot and resolve this issue?

Answer №1

To achieve in Next.js 13, make sure to render jsx after the component has been mounted


function CustomComponent() {
  const [isMounted, setIsMounted] = useState(false);
  useEffect(() => {
    setIsMounted(true);
  }, []);

  if (!isMounted) return <></>;
  // continue with your code implementation
}

Answer №2

For a temporary fix, I have implemented a workaround that resolves the issue but sacrifices SSR functionality.

By utilizing a dynamic import on my ContextProvider, the server-rendering is disabled and the error disappears. This also eliminates the flashing problem between dark and light themes stored in localStorage. However, it does mean that SSR benefits are relinquished. If anyone has a better solution, please share it.

import '@styles/globals.scss';
import { GlobalContent } from '@components/GlobalContent/GlobalContent';
import { Inter } from '@next/font/google';
import dynamic from 'next/dynamic';
import { ReactNode } from 'react';

const inter = Inter({ subsets: ['latin'] });

interface RootLayoutProps {
  children: ReactNode
}

// Fixes: Hydration failed because the initial UI does not match what was rendered on the server.
const DynamicContextProvider = dynamic(() => import('@components/ContextProvider/ContextProvider').then(mod => mod.ContextProvider), {
  ssr: false
});

const RootLayout = ({ children }: RootLayoutProps) => {
  return (
    <html lang="en" className={inter.className}>
      <head />
      <body>
        <DynamicContextProvider>
          <GlobalContent>
            {children}
          </GlobalContent>
        </DynamicContextProvider>
      </body>
    </html>
  );
};

export default RootLayout;

This workaround does not globally disable SSR. As an experiment, I created a new test page with the code below:

async function getData() {
  const res = await fetch('https://rickandmortyapi.com/api/character', { cache: 'no-store' });
  if (!res.ok) {
    throw new Error('Failed to fetch data');
  }

  return res.json();
}

export default async function Page() {
  const data = await getData();

  return (
    <main>
      {data.results.map((c: any) => {
        return (
          <p key={c.id}>{c.name}</p>
        );
      })}
    </main>
  );
}

After executing npm run build, the test page confirms the usage of SSR.

https://i.stack.imgur.com/sJYuU.png

Upon inspecting the response for the test page, it reveals the HTML response received.

https://i.stack.imgur.com/YWpwg.png

Answer №3

Encountered a similar issue while trying to integrate the Navbar component into RootLayout within layout.tsx in Next.js 13.

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
       <Navbar />
      <body className={font.className}>
        {children}
      </body>
    </html>
  );
}

I made a modification to the code above and placed the Navbar inside the body tag, which resolved the issue.

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <body className={font.className}>
        <Navbar />
        {children}
      </body>
    </html>
  );
}

Answer №4

The error was resolved by dynamically importing the default export of ContextProvider in _app.tsx. The context state is also being persisted in localStorage without any issues.

_app.tsx

import dynamic from "next/dynamic";
 
const TodoProvider = dynamic(
  () => import("@/util/context").then((ctx) => ctx.default),
  {
    ssr: false,
  }
);

export default function MyApp({ Component, pageProps }: AppProps) {
  return (
    <TodoProvider>
      <Component {...pageProps} />
    </TodoProvider>
  );
}

context.tsx

import React, {
  useState,
  FC,
  createContext,
  ReactNode,
  useEffect,
} from "react";

export const TodoContext = createContext<TodoContextType | null>(null);

interface TodoProvider {
  children: ReactNode;
}

const getInitialState = () => {
  if (typeof window !== "undefined") {
    const todos = localStorage.getItem("todos");
    if (todos) {
      return JSON.parse(todos);
    } else {
      return [];
    }
  }
};

const TodoProvider: FC<TodoProvider> = ({ children }) => {
  const [todos, setTodos] = useState<ITodo[] | []>(getInitialState);
  const saveTodo = (todo: ITodo) => {
    const newTodo: ITodo = {
      id: Math.random(),
      title: todo.title,
      description: todo.description,
      status: false,
    };
    setTodos([...todos, newTodo]);
  };
  const updateTodo = (id: number) => {
    todos.filter((todo: ITodo) => {
      if (todo.id === id) {
        todo.status = !todo.status;
        setTodos([...todos]);
      }
    });
  };

  useEffect(() => {
    if (typeof window !== "undefined") {
      localStorage.setItem("todos", JSON.stringify(todos));
    }
  }, [todos]);

  return (
    <TodoContext.Provider value={{ todos, saveTodo, updateTodo }}>
      {children}
    </TodoContext.Provider>
  );
};

export default TodoProvider;

Answer №5

Encountering an issue with the popover component due to HTML tag nesting:

  <Popover open={open}>
    <PopoverTrigger>
      ...

To resolve, use the asChild prop like so:

For example;

  <Popover open={open} onOpenChange={setOpen}>
            <PopoverTrigger asChild>
                <Button
                    variant="outline"
                    size="sm"
                    role="combobox"
                    aria-expanded={open}
                    aria-label="Select a store"
                    className={cn("w-[200px] justify-between", className)}
                >

Credits to AissaSemaoui's input here

Answer №6

When using ShadCn, make sure to place the ThemeProvider tag within the html and body tags

Answer №7

During my development process, I found myself working with nextjs version 14 and nexui version 2.

I discovered that placing my providers directly inside the body tag of the root component resolved the issue for me.

The relevant files where these changes were made are located at "@/app/layout.tsx".

export default async function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {

  const session = await getServerSession(authOptions)
  return (
    <html lang="en">
      <body className={inter.className}>
        <Providers session={session}>
          {children}
        </Providers>
      </body>

    </html>
  );
}

The modifications to improve the structure can be found in the file "@/app/providers.tsx".

export function Providers({ children, session }: { children: React.ReactNode, session: any }) {
    return (
        <SessionProvider session={session}>
            <NextUIProvider>
                {children}
            </NextUIProvider>
        </SessionProvider>
    );
}

Answer №8

The primary layout structure is determined at the highest level of the application directory and is applicable to all routes. A fundamental aspect of this layout revolves around the inclusion of html and body tags, enabling users to customize the original HTML content provided by the server.

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body>
        {/* Designing the Layout */}
        <main>{children}</main>
      </body>
    </html>
  )
}

Moreover, layouts within the folder hierarchy are typically organized in a hierarchical fashion, signifying that they encapsulate child layouts through their children property. To establish nested layouts, simply insert a layout.js file within specific route segments (folders). The template for achieving this structure would resemble:

export default function DashboardLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return <section>{children}</section>
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Why does my console refuse to log the input entered for the search?

Looking to become proficient in web development, I am attempting to record HTML search queries in the console after storing them in a variable. However, when I try running the search procedure, nothing seems to be displaying in my browser's inspect co ...

What is the best way to test a Redux dumb component that displays smart components?

One way to test a "dumb" React view is by supplying it with props and using tools like enzyme or jsdom. Snapshot testing with jest can then be used to confirm the behavior. For a smart component made up of a 'dumb view' connected with mapStateTo ...

Adding a Scrollbar to an Extensive Organizational Chart Made with react-d3-tree

Utilizing the react-d3-tree library, I have successfully implemented an organizational chart (org chart) within my React application. The org chart functions well with smaller datasets, but as the organization expands, I am encountering the challenge of ac ...

JavaScript Asynchronous Functions Not Handling Await Calls Correctly

The two fadeInList functions control the fading animation of a continuous list split into two lines. The typeOutText function displays text and is supposed to execute List1 first, wait for it to finish, and then proceed with List2. However, after adding ke ...

Describing a function in Typescript that takes an array of functions as input, and outputs an array containing the return types of each function

Can the code snippet below be accurately typed? function determineElementTypes(...array: Array<(() => string) | (() => number) | (() => {prop: string}) | (() => number[])>) { /// .. do something /// .. and then return an array ...

What is the best method to utilize a promise to delay the execution of a function until the data is received and stored

Currently, I am facing an issue with my API where the model variable is returning undefined before any data is populated in the return_array. I am unsure of how to implement promises or another method to ensure that the variable waits for data to be fille ...

Trouble confirming the password field with regular expressions in angular.js

I'm trying to validate my password field with specific special characters requirements. The field must contain at least one number, upper case letter, lower case letter, and an underscore, all of which are mandatory. I have attempted to achieve this u ...

Storing a date in MySQL using Vue.js and Node.js

My current tech stack consists of nodejs and express.js for the backend, vuejs for the frontend, and mysql as the database. I am facing an issue where I cannot send a date retrieved from localStorage to my mysql database. Whenever I try to send the date, ...

Issue with activating a Modal through a button inside a table row on React

I'm currently working on two files: Modal.js and Users.js. Users.js features a table with an API get query linked to it, and in the last column of the table, there's a dropdown for each row that contains three buttons: View, Edit, and Delete. My ...

Navigating CSV-derived JSON data in Flask and Javascript: Best Practices

My current goal is to read a CSV file on the backend using Python/Flask and then display its data as an HTML table with Javascript. I have simplified my task to just displaying JSON values passed from Python in the browser console, which will help me build ...

Is there a way to retrieve all active HTTP connections on my Express.js server?

In my express server app, I am implementing SSE (server send events) to inform clients about certain events. Below is the code snippet from my server: sseRouter.get("/stream", (req, res) => { sse.init(req, res); }); let streamCount = 0; class SS ...

What is the best way to modify or revise meta fields for individual products in order to retrieve multiple images for each product in Shopify?

What is the process for updating or editing meta fields to display multiple images of each product on Shopify? ...

Performing AJAX in Rails4 to update boolean values remotely

Suppose I have a model named Post which includes a boolean attribute called active. How can I efficiently modify this attribute to either true or false directly from the list of posts in index.html.erb using link_to or button_to helper along with remote: ...

Unable to get CSS transition to function properly after adding a class using jQuery

I am trying to create a hover effect that shows an image when the mouse is over a specific div, but for some reason, the transition is not working as expected. I understand that using visibility: hidden cannot be animated. Here is the code snippet: $(d ...

What is causing myInterval to not be cleared properly?

const homeButton = document.getElementById('home'); window.myInterval = 0; const showHome = () => { console.log('showHome'); window.myInterval = setInterval(wait, 400) } const wait = () => { console.log('wait'); ...

Divide text to reduce its width within the confines of a specific height on a div element

I've spent the past week scouring various forums, including stackoverflow, but I haven't been able to find a solution. In my responsive website, I'm using CSS flexbox to display dynamic menu items. Sometimes the text can be quite long, and ...

Load link dynamically using the rel attribute

I am trying to implement dynamic content loading using jQuery's .load() function. The links are stored in the .rel attribute of the anchor tags. My setup looks like this: <script> $(document).ready(function(){ $('.sidebar_link').clic ...

What is the most effective method for importing .module.scss classes in a React project?

In the midst of my NextJs project, I have encountered two different methods for importing CSS classes from a .module.scss file. Method 1: import * as styles from './page.module.scss'; Classes are used like this: <div className={styles.myCla ...

Is there a way to make this eval() function function properly in Internet Explorer?

My JavaScript code is fetching another JavaScript "class" from a different XHTML page. The fetched JavaScript looks like this: (function() { this.init = function() { jQuery("#__BALLOONS__tabs").tabs(); }; }) Once the f ...

"Have you ever wondered how the router object in express.js is seamlessly integrated with app.use(), considering that it only accepts

I am curious about the process of how the router object in express.js is passed to app.use(), which typically only accepts callbacks. Since router is an object of express, I am trying to understand why app.use() does not throw an error even though it req ...