useMediaQuerySSR()

Media queries done right with SSR


This custom hook solves SSR hydration problem, if you are using Chakra, MAterial, Emotion and your styles are hydrated improperly, you get a warning Warning: Did not expect server HTML to contain a <div> in <div>. or any other element, you are probably running some code that's meant to be client side only, t.e useMediaQuery is one of them, cause it needs a window obj.

/**
 * Accepts query string in format like Window.matchMedia()
 * https://developer.mozilla.org/en-US/docs/Web/API/Window/matchMedia
 *  EXP: '(max-width: 600px)' '(min-width: 600px)'
 *
 *
 * Return Boolean, based on query params
 */
import { useLayoutEffect, useEffect, useState, useCallback } from 'react';

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

const useMediaQuerySSR = (queryStr) => {
  const [targetW, setTargetW] = useState(false);

  const updateTarget = useCallback((e) => {
    if (e.matches) {
      setTargetW(true);
    } else {
      setTargetW(false);
    }
  }, []);

  useIsomorphicLayoutEffect(() => {
    const media = window.matchMedia(queryStr);

    media.addEventListener('change', updateTarget);

    // Check on mount (callback is not called until a change occurs)
    if (media.matches) {
      setTargetW(true);
    }
    // clean up
    return () => media.removeEventListener('change', updateTarget);
  }, []);

  return [targetW];
};

export { useMediaQuerySSR };