Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React SSR, Proper way of handling page scroll position

My goal is to apply different className depending on the user's scroll position. Well, I need to change the background color of the navbar if the user's scroll position is > 0. I came up with a working solution that works in all cases except the one if the user loads a page and initial scroll position is not 0 (scrolled and then reloaded the page).

What I did is I created a custom hook which looks like this:

import { useState, useEffect } from 'react';

export default () => {
  const [scrollPosition, setScrollPosition] = useState(
    typeof window !== 'undefined' ? window.scrollY : 0,
  );

  useEffect(() => {
    const setScollPositionCallback = () => setScrollPosition(window.scrollY);

    if (typeof window !== 'undefined') {
      window.addEventListener('scroll', setScollPositionCallback);
    }

    return () => {
      if (typeof window !== 'undefined') {
        window.removeEventListener('scroll', setScollPositionCallback);
      }
    };
  }, []);

  return scrollPosition;
};

And then I use this hook in my Navbar component:

...
const scrollPosition = useScrollPosition();
...

<Navbar
  color={scrollPosition < 1 ? 'transparent' : 'white'}
  ...
/>

As I described, everything works well if the user loads the page at the 0 scrollY. When it's not, I get the warning Warning: Prop className did not match, which is expected, because scrollY is always 0 on the server side and then scrolling doesn't work, because Navbar keeps ssr class.

What is the proper way of handling it?

like image 239
Sergey Avatar asked Oct 23 '25 17:10

Sergey


1 Answers

I've found a solution. The reason why it was happening is, due to this line in the hook:

const [scrollPosition, setScrollPosition] = useState(
  typeof window !== 'undefined' ? window.scrollY : 0,
);

scroll position was equal to 0 all the time on ssr, but when loaded on the client side, it was set to actual scrollY at the beginning.

So what I did is I set initial scrollPosition to 0 on both client and server side by modifying the line below to:

const [scrollPosition, setScrollPosition] = useState(0);

and the added one more effect that works on client side only, which sets scrollPosition:

useEffect(() => {
  if (typeof window !== 'undefined' && window.scrollY !== 0) {
    setScrollPosition(window.scrollY);
  }
}, []);
like image 66
Sergey Avatar answered Oct 25 '25 06:10

Sergey