Encountering a hydration error with the useResponsive hook in Nextjs Material UI

I am facing an issue with a useResponsive hook that relies on useMediaQuery from the Material-UI library. While using Next.js with app router, the initial value of the hook returns false and then transitions to true, causing a hydration mismatch. How can I resolve this?

Here is the code snippet:

useResponsive.ts

import { useMediaQuery, useTheme } from '@mui/material';
import { useEffect, useLayoutEffect } from 'react';

export interface IBreakpointsType {
  isMobile: boolean;
  isTablet: boolean;
  isSmallLaptop: boolean;
  isLargeLaptop: boolean;
  isDesktop: boolean;
  isComputer: boolean;
  isLaptop: boolean;
}

const useIsomorphicLayoutEffect =
    typeof window !== 'undefined' ? useLayoutEffect : useEffect

const useResponsive = () => {
  const theme = useTheme();
  const isMobile = useMediaQuery(theme.breakpoints.down('sm'));
  const isTablet = useMediaQuery(theme.breakpoints.between('md', 'lg'));
  const isSmallLaptop = useMediaQuery(theme.breakpoints.between('lg', 'xl'));
  const isLargeLaptop = useMediaQuery(theme.breakpoints.between('xl', 'xxl'));
  const isDesktop = useMediaQuery(theme.breakpoints.up('xxl'));
  const isComputer = useMediaQuery(theme.breakpoints.up('lg'));
  const isLaptop = useMediaQuery(theme.breakpoints.between('md', 'xl'));

  useIsomorphicLayoutEffect(() => {
    const handleResize = () => {
      window.dispatchEvent(new Event('resize'));
    };

    window.addEventListener('themeChange', handleResize);

    return () => {
      window.removeEventListener('themeChange', handleResize);
    };
  }, []);

  return {
    isMobile,
    isTablet,
    isSmallLaptop,
    isLargeLaptop,
    isDesktop,
    isComputer,
    isLaptop,
  };
};

export default useResponsive;

The hook implementation in Footer.tsx:

Footer.tsx

'use client';
import {
  AppBar,
  Box,
  Divider,
  Grid,
  Link,
  Stack,
  Toolbar,
  Typography,
} from '@mui/material';
import React from 'react';

import useResponsive from '../../hooks/useResponsive';

import Flex from '../Flex';

import StyledFooterLogo from './FooterLogo';

const Footer = () => {
  const { isComputer } = useResponsive();
  console.log('isComputer', isComputer)
  return (
    <AppBar
      component="footer"
      sx={{ top: 'auto', bottom: 0, backgroundColor: '#EEEEEE' }}
    >
      <Toolbar variant="dense">
        <Grid
          container
          spacing={{ xs: 1, sm: 2, md: 4 }}
          sx={{ alignItems: 'center', width: '100%' }}
        >
          <Grid item xs={12} sm={6}>
            <Box
              display={isComputer ? 'flex' : 'block'}
              alignItems="center"
              sx={{
                justifyContent: isComputer ? 'left' : 'center',
                '& hr': {
                  mx: 1,
                },
              }}
            >
              <Stack
                direction={isComputer ? 'row' : 'column'}
                sx={{ alignItems: 'center', width: '100%' }}
              >
                <StyledFooterLogo />
                {isComputer && <Divider orientation="vertical" flexItem />}
                <Typography
                  variant="body1"
                  component={Flex}
                  sx={{ justifyContent: 'center' }}
                  className="footerRights"
                >
                  &copy; &nbsp;
                  {new Date().getFullYear()}.All rights reserved.
                </Typography>
              </Stack>
            </Box>
          </Grid>
          <Grid item xs={12} sm={6}>
            <Stack
              direction={isComputer ? 'row' : 'column'}
              spacing={2}
              sx={{
                justifyContent: isComputer ? 'flex-end' : 'center',
                alignItems: 'center',
              }}
            >
              <Link
                href="/privacy-policy/"
                style={{ textDecoration: 'none' }}
                target="_blank"
              >
                <Typography variant="body1" className="footerRights">
                  Privacy Policy
                </Typography>
              </Link>
              <Link
                href="/about/"
                style={{ textDecoration: 'none' }}
                target="_blank"
              >
                <Typography variant="body1" className="footerRights">
                  About
                </Typography>
              </Link>
              <Link
                href="/contact/"
                style={{ textDecoration: 'none' }}
                target="_blank"
              >
                <Typography variant="body1" className="footerRights">
                  Contact
                </Typography>
              </Link>
            </Stack>
          </Grid>
        </Grid>
      </Toolbar>
    </AppBar>
  );
};

export default Footer;

Your advice on resolving this issue would be highly appreciated.

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

Answer №1

One solution could be to encapsulate your components within a Client Only component.

Check out the implementation of the ClientOnly component below:

'use client'

import * as React from 'react'

export default function ClientOnly({ children }: { children: React.ReactNode }) {
  const [isMounted, setIsMounted] = React.useState(false)

  React.useEffect(() => {
    setIsMounted(true)
  }, [])

  if (!isMounted) return null

  return children
}

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

Having difficulties achieving successful API requests in Next.js and Snipcart

I'm currently diving into the world of Snipcart, and I'm encountering some issues connecting to your API. I'm using Next.js and haven't been able to find any solutions on the forum or in the documentation that address my specific proble ...

How can I adjust the vertical position of Material-UI Popper element using the popper.js library?

https://i.stack.imgur.com/ZUYa4.png Utilizing a material-ui (v 4.9.5) Popper for a pop-out menu similar to the one shown above has been my recent project. The anchorElement is set as the chosen ListItem on the left side. My goal is to have the Popper alig ...

Map failing to refresh

Having some trouble with the map function as it's not updating my select box with the new selected value. The issue occurs within a material UI dialog that appears when viewing a file. I notice that the values get updated only after closing and reopen ...

What is the method for retrieving the locale value from the configuration in Next.js?

How can I retrieve the i18n.defaultLocale value from my Next.js app configuration? I thought it would be simple, but I'm struggling to find a clear solution for accessing the config settings in Next.js. Is there a specific built-in method for this, or ...

What is the best method to display a tooltip for a disabled radio button within a set of radio buttons?

Is there a way to disable a specific radio button based on a condition and display a tooltip only for that disabled button? https://i.stack.imgur.com/niZK1.png import {Tooltip} from '@mui/material'; <Tooltip titl ...

Electronic circuit embedded within a material-textured text field offering multiline functionality

While experimenting with TagsInput, I came across this helpful snippet on codesandbox that you can check out here. The challenge I encountered is when there are numerous chips, they extend beyond the boundaries of the text field. My goal is to implement ...

Attempting to display items using the map method, pulling in text from an array

I am working with an array state that tracks the text entered by the user in a text field. My goal is to display this text within a component so users can see what they have previously entered. However, I am facing an issue with my Hashtags component when ...

Designing an interactive header interface using React and Material UI

My header.jsx file contains the following code: // Default Import Statements var Login = require(login.jsx) const HeaderComponent = React.createClass({ getInitialState () { return { loggedIn: false, }; }, render() { return ( ...

Does Next.js pre-render every page, or does it only pre-render the initial page?

As I dive into the world of nextjs, I'm coming across conflicting information. Some sources claim that nextjs only prerenders the first page, while others suggest that all pages are prerendered by default. This contradiction has left me confused about ...

The art of replacing material-ui styles with styled components

As a newcomer to UI material design, I am eager to create my own customized Button Component using styled-components. I am facing a challenge in overriding the CSS based on different button variations such as "primary" or "secondary". You can find my cod ...

I am struggling to apply custom CSS styles to the scrollbar within a Card component using MUI react

import React from "react"; import Card from "@mui/material/Card"; import CardActions from "@mui/material/CardActions"; import CardContent from "@mui/material/CardContent"; import CardMedia from "@mui/material/Ca ...

Choosing a root element in a hierarchy without affecting the chosen style of a child

I am working on a MUI TreeView component that includes Categories as parents and Articles as children. Whenever I select a child item, it gets styled with a "selected" status. However, when I click on a parent item, the previously selected child loses its ...

Switch the visibility of a div tag using Next.js

Script export default function Navigation(){ let displayMenu = false; function toggleMenu() { displayMenu = !displayMenu; } return ( <> <button onClick={toggleMenu}>Toggle Navigation</button> {/*This code sh ...

Removing chips in Material UI can be easily accomplished by following these steps

Recently, I implemented a feature where chips are generated as the user types in a text field and clicks on create. A chip is then displayed with the entered text. Now, I am looking to add the ability to delete these chips dynamically. You can view the s ...

What is the best method for testing routes implemented with the application router in NextJS? My go-to testing tool for this is vitest

Is it possible to test routes with vitest on Next.js version 14.1.0? I have been unable to find any information on this topic. I am looking for suggestions on how to implement these tests in my project, similar to the way I did with Jest and React Router ...

I'm wondering, on a storybook, where is the best place to set my MUI X license key

Can you help with where to specify my license key in Storybook? I need guidance on where to set the license key in Storybook. According to MUI Docs, it should be done before React renders the first component and only once in the application. However, I ...

Utilizing client-side properties in an onClick event within Next.js framework

class homeNotLoggedIn extends React.Component { componentDidMount() { function logout(){ localStorage.clear() location.reload() } } render() { return ( <div> < ...

What is the function of async in Next.js when triggered by an onClick

Need help with calling an async function pushData() from a button onClick event async function pushData() { alert("wee"); console.log("pushing data"); try { await query(` //SQL CODE `); console.log("Done&quo ...

The height of the Material UI Paper component is not appropriately matched with the parent component

I am currently working with the Paper component that contains a Card component, and I am trying to make its height fill the entire screen. To simplify the problem, I have provided the following code: import React from "react"; import { makeStyles ...

Is there a way to extract a token from the URL in a Nextjs/React application?

I am currently developing a project that relies heavily on API integration. My front-end interface is built using React and Next.js, while the back-end API is developed with Laravel. Within my front-end page, I have implemented a token in the URL to help ...