Automatically Scroll to Top of Page With React useLayoutEffect

We'll see how React's useLayoutEffect Hook can help have our application window be scrolled to the top when we navigate from page to page.

Project Source Code

Get the project source code below, and follow along with the lesson material.

Download Project Source Code

To set up the project on your local machine, please follow the directions provided in the README.md file. If you run into any issues with running the project source code, then feel free to reach out to the author in the course's Discord channel.

This lesson preview is part of the TinyHouse: A Fullstack React Masterclass with TypeScript and GraphQL - Part Two course and can be unlocked immediately with a \newline Pro subscription or a single-time purchase. Already have access to this course? Log in here.

This video is available to students only
Unlock This Course

Get unlimited access to TinyHouse: A Fullstack React Masterclass with TypeScript and GraphQL - Part Two, plus 70+ \newline books, guides and courses with the \newline Pro subscription.

Thumbnail for the \newline course TinyHouse: A Fullstack React Masterclass with TypeScript and GraphQL - Part Two

One small, I won't necessarily say bug, but one small improvement we can make can sort of be noticed when we, let's say, scroll down within a certain page, like the homepage. And if we were to click another page in a wrap, like the host page, we'll notice that we're still in the scroll to down position. And from a user perspective, we think this is a little jarring. You know, usually we want people to remain at the top of every page when they navigate to that page. Why is this sort of happening? Now, if you recall, this is a single page app, the server only returns a single bundle at the very beginning. Once that happens, React or JavaScript is basically taking control to show the different components. And when we navigate between components to components, the actual scroll position of our window remains the same, right? So there's a few ways we can handle something like this. One simple way we can think about solving this is perhaps when we navigate between different routes at any moment in time and we navigate to another route, we just should move to the window scroll position to the very top, right? Or another way we can think about this is when any of these components get rendered, let's say the home component gets rendered here or the host component gets rendered at any moment in time when these components get shown for the very first time, we want to move the scroll position of the window to the very top. There's perhaps many different ways we can accomplish something like this. We can see if React or RADA provides a support for this. We can see if maybe on our own, if a route ever changes on the route level, you know, scroll the user to the top. However, in our context, we're going to keep things very simple. And the way we're going to do this is basically say whenever one of these section page level components, like the home components or the host component get rendered, we'll simply, you know, run an effect and scroll the window to the very top of the screen. When it comes to running effects, we've seen how the use effect hook is the primary hook for this. In this particular use case, we're going to use another hook from React called useLayout effect. The useLayout effect from the React documentation tells us that the signature is identical to the use effect hook, so the way we use it is practically the same. However, it fires synchronously after all DOM mutations. And we should use this to read layout from the DOM and synchronously re-render. So in this particular context, if we're ever making changes to the DOM or we need to know the position of certain things in the DOM, like the scroll position or the window position, et cetera, we should always use the useLayout effect hook. The use effect hook basically should only be used if nothing dependent on the DOM needs to be done. So, you know, make a fetch request to the server or, you know, update component states, all of those particular changes are independent of the DOM. We don't really care about the DOM. However, the moments we need to do something that depends on the actual DOM, we should use the useLayout effect hook. So let's actually see this in action. We'll head back to our client side code. Now, the first thing that came to mind in our case was, why don't we just have this being done on the parent app component, you know, just use the useLayout effect hook here. And technically, when we go from page to page with the app component in that context, recognize that we should scroll and run the effect, it would work in this context. However, keep in mind the parent app component is the component that's rendered at any moment in time. So if we were to use the useLayout effect here, we'd have to run the effect when the route changes when in the app component. The other way of doing it and the way we'll go about doing it is simply just use the effect within the individual section components. So in that context, when we go to the host route, the host component should render. And if that is to happen, we can then run the effect to scroll to the top. And we can repeat this for the listing component, the listings, et cetera. Once we're going to do this in a few different areas and though the code we're going to write is still very small, we can try to consolidate this to a hook of our own. And we'll create this hook, let's say, in the source lib folder and we'll create a new folder for where we will essentially say all the hooks or the custom hooks we 'll create will be kept here. And we'll continue with the same folder structure we've done before. We'll have a folder for the new hook we're creating and we'll call this hook use scroll to top. And we'll give this an index.ts file or sorry index.ts file where I'm rendering JSX here. And in the hooks folder, we'll simply look to exports whatever we attempt to create from the use scroll to top index file. So in this use scroll to top index file, the only hook we'll need from react is the use layout effect hook. And we can export a function that we'll call use scroll to top. That will receive no arguments and we'll look to use the layout effect hook. And in this context, the way we want to use it is we only want to use it for the individual components on initial render, so we'll provide an empty dependency array. In this case, what we can do in our particular effect callback is we can use the window object and we can use the scroll to method to essentially scroll the user to the very top of the screen by providing the zero zero arguments. The scroll to method of the window object essentially receives coordinate values for the x axis and y axis of the web page by saying zero zero, we're essentially scrolling to the very beginning of the very top left hand corner of the page, which should be the very beginning of the page. So now with this hook created, we can try to go to some of these individual section level components. Let's say we'll try out the home component first. And we'll look to import the actual use scroll to top hook. So here, right before our util section, we'll import use scroll to top function hook from the lib hooks file. And somewhere at the very beginning of our home component function, we can just simply run the use scroll to top function. At this very moment, if we were to take a look at our client application, if I was to go to, let's say, the host route or the host component, scroll down to the very bottom, and then try to navigate to the index route with the home page, we'll be taken to the very top. So our use scroll to top hook works as intended. And we're using the window scroll function to essentially scroll the user to the very beginning of the page. So now let's try to have the same behavior in the section level components available in our app. We'll just copy the import we have here. And we'll simply look to visit the section level components that are rendered per route. So we'll go to the host components for the host route. We'll import the use scroll to top method hook, and we'll look to use it in our component function. We'll go to the listing page, or that that's being shown in the listing route. We'll import the use scroll to top method or hook, and we'll simply run it in our component function. We'll go to the listings component. We'll import the use scroll to top hook. And we'll also use it in the components function. We'll go to the login component as well. Now for some of these components, we don't even have the capability to scroll, but we'll be consistent and we'll look to display it in every single section level components. We'll go to the stripe component as well, where in this component there isn't any capability to scroll as well, but we'll keep it consistent as well. And finally, we'll go to the user component that's being shown for a user route , and we'll import the use scroll to top hook, and we'll use it in the component function. So now at this moment in time, in my client application, if I was to scroll down the same the home page, click the host route will be taken to the top. If I was to scroll down in the host route, navigate to the user page will be taken to the top as well. But however, in the user page, the network request is always being made, so you might just be defaulted to the top anyway. If I was to move backwards, let's say go from the host page, let's go from the home page actually to a certain listing page will be in the top as well. If I was to scroll down in the host page and try to visit a certain city in the app header, say San Francisco, will be taken to the top as well. But in this case as well, there isn't any scroll capability. We're just defaulted to the top, but we've applied it to every section level component anyway. Now, to reiterate once again, we're okay with using this approach of just essentially placing the hook in the section level components. Primarily for the fact that we don't have a ridiculous number of section level components. Now, if we were to have dozens and dozens of them, we would probably conform to maybe using just the layout effect hook on a route basis. So whenever the route changes, to make sure you scroll to the top. But in this case, this does the job that we want. And at this moment in time, we'll just make sure that every single time we go to a certain router page, the user is taken to the top of the screen. [BLANK_AUDIO]