Skip to content

Instantly share code, notes, and snippets.

@romanonthego
Last active July 13, 2022 10:52
Show Gist options
  • Save romanonthego/223d2efe17b72098326c82718f283adb to your computer and use it in GitHub Desktop.
Save romanonthego/223d2efe17b72098326c82718f283adb to your computer and use it in GitHub Desktop.
Scroll to top with react hooks
import React, { useEffect } from 'react';
import { useRouter } from 'state';
// Component that attaches scroll to top hanler on router change
// renders nothing, just attaches side effects
export const ScrollToTopControlller = () => {
// this assumes that current router state is accessed via hook
// but it does not matter, pathname and search (or that ever) may come from props, context, etc.
const { pathname, search } = useRouter();
// just run the effect on pathname and/or search change
useEffect(() => {
try {
// trying to use new API - https://developer.mozilla.org/en-US/docs/Web/API/Window/scrollTo
window.scroll({
top: 0,
left: 0,
behavior: 'smooth',
});
} catch (error) {
// just a fallback for older browsers
window.scrollTo(0, 0);
}
}, [pathname, search]);
// renders nothing, since nothing is needed
return null;
};
@TSMMark
Copy link

TSMMark commented Oct 30, 2019

Thanks for posting this, but I have a concern which you may not have considered, based on the assumption you would place this relatively top-level in the application JSX, around where application providers would typically live.

If that's the case, doesn't this scroll the window to the top on initial pageload? Is that ideal? Shouldn't it be on page change? What about hash/fragments that are intended to scroll the window down to a specific element (default browser behavior)?

You may want to consider firing the effect after pathname or search have changed. I'm not sure if this is ideal or correct but something to consider:

  useEffect(() => () => { // <-- Now we return the useEffect teardown effect, which will be fired only after the path/search change for the first time
    try {
      // trying to use new API - https://developer.mozilla.org/en-US/docs/Web/API/Window/scrollTo
      window.scroll({
        top: 0,
        left: 0,
        behavior: 'smooth',
      });
    } catch (error) {
      // just a fallback for older browsers
      window.scrollTo(0, 0);
    }
  }, [pathname, search]);

@ultim8k
Copy link

ultim8k commented Jul 13, 2022

Smooth scroll doesn't always work for some reason. A workaround is to wrap it in a setTimeout with zero time. This forces it to run after dom is ready after the render (I think).

setTimeout(() => {
    window.scroll({
        top: 0,
        left: 0,
        behavior: 'smooth',
    });
}, 0);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment