Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NextJS & SSR Bug: Warning: Prop className did not match on useMediaQuery #4319

Closed
1 of 3 tasks
niconiahi opened this issue Jul 2, 2021 · 6 comments
Closed
1 of 3 tasks
Labels
needs triage Issues and pull requests that need triage attention Type: Bug 🐛 Something isn't working

Comments

@niconiahi
Copy link

Description

On a NextJS application, when using Chakra's useMediaQuery to determine a value for a Text prop, it prints an error about a non matching className on the server and the client

Link to Reproduction

https://codesandbox.io/s/festive-keller-6jhy5

Steps to reproduce

IN THE SANDBOX WORKS:

  1. Run the code and check the console

IN MY NEXTJS APP WITH CHAKRA DOES NOT.
Am I missing something?

Code reference (same as codesandbox but in real app)
same happening to me when ussing useMediaQuery as so:

const useDevice = () => {
  const [isLargeDevice] = useMediaQuery('(min-width: 62em)')

  return {
    isLargeDevice,
  }
}

then used: (THIS THROWS AN ERROR)

  import { useDevice } from './useDevice'
  
  const { isLargeDevice } = useDevice()
  
   <Text color='white' textStyle={isLargeDevice ? 'body.bold.lg' : 'body.bold.md'}>
       Don't miss out on new proposals
   </Text>

used: (THIS DOESN'T)

   <Text color='white'>
       Don't miss out on new proposals
   </Text>

screenshot here:
image

Chakra UI Version

1.6.4

Browser

Google Chrome 91.0.4472.114

Operating System

  • macOS
  • Windows
  • Linux

Additional Information

Relates to this issue:

#684

@niconiahi niconiahi added needs triage Issues and pull requests that need triage attention Type: Bug 🐛 Something isn't working labels Jul 2, 2021
@segunadebayo
Copy link
Member

Hi @niconiahi,

As mentioned in the docs,

Keep in mind this API relies on the user's browser support of window.matchMedia and will always return false if it is not supported or does not exist (e.g. during server-side rendering).

To fix this, I recommend waiting for the client to be re-hydrated by using a useEffect check, then you can return the value from useBreakpointValue

const [mounted, setMounted] = useState(false)
const value = useMediaquery(...)

useEffect(()=>{ 
  setMounted(true)
},[])

return mounted ? value : null

Be sure to add a spinner or skeleton to that portion of the page so there's no layout shift.

@aboveyunhai
Copy link

@segunadebayo maybe we should include your suggestion implementation in doc or inside the example code block?

Or you consider it might mislead some people how to use it without specific concern.
I felt like it should be indicated somewhere, it's pretty ambiguous with the text mentioned about SSR issues only.

@kimgysen
Copy link

kimgysen commented Feb 1, 2022

So there is a mismatch in the className between server and client, and the solution is to add a spinner and set the value with useEffect to fix a media query? Maybe it's me, but that doesn't sound right.

@oiojin831
Copy link

It seems not working when I do it with remix..
any suggestion?

@aboveyunhai
Copy link

aboveyunhai commented Feb 4, 2022

So there is a mismatch in the className between server and client, and the solution is to add a spinner and set the value with useEffect to fix a media query? Maybe it's me, but that doesn't sound right.

The idea of this code is to avoid rendering the hook value in server side because server does not have media query. didMount in useEffect is to enforce the hook value is only used in the client side. Nothing to do with loader. Spinner is just to make it look nicer and smoother in terms of UI/UX.

This particular hook is client side only. That's why I asked to include the implementation in doc, instead of using one line of text to mention it doesn't work in server side rendering. But depending on the readers' background knowledge, this also might not immediately obvious for them.
It requires you to know a little bit of SSR, CSR and some framework (like next.js) rendering process.

@oleksandr-danylchenko
Copy link

oleksandr-danylchenko commented Feb 18, 2023

In my project I came up with a hook that can wrap the value that leads to a server-client mismatch:

export const useClientSideValue = <T>(value: T, ssrValue: T): T => {
  // Needed to prevent a mismatch between the first render on the server and the client
  const [mountedValue, setMountedValue] = useState(ssrValue);
  useEffect(() => setMountedValue(value), [value]);

  return mountedValue;
};

Usage:

const clientValue = useMediaquery(...);
const value = useClientSideValue(clientValue, '');

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
needs triage Issues and pull requests that need triage attention Type: Bug 🐛 Something isn't working
Projects
None yet
Development

No branches or pull requests

6 participants