Scrolling to the top of a page can be a useful feature to include in your Next.js website or application. It can be especially helpful when a user has scrolled down a long page and wants to quickly return to the top. In this article, we’ll explore how to implement this in Next.js. We also have an article covering this in Angular here.

Introduction

Here’s the page we’ll be using. It’s a simple design with an extremely long div, in practice, this might be something like a blog post, a shopping site, or anything with a lot of content.

And the code for our page:

export default function ScrollToTopPage() {
    return (
        <div className="relative flex h-[500vh] w-full flex-col items-center justify-center bg-stone-900 text-3xl text-white">
            <ul className="absolute flex h-full flex-col items-center justify-between p-20">
                <li> This is a really long div...</li>
                <li>Halfway there!</li>
                <li>👋</li>
            </ul>
        </div>
    );
}

Button

Now let’s add in a scroll to bottom button through the navigator API.

Here’s the function we’ll be using:

const isBrowser = () => typeof window !== 'undefined'; //The approach recommended by Next.js

function scrollToTop() {
    if (!isBrowser()) return;
    window.scrollTo({ top: 0, behavior: 'smooth' });
}

The first part is just a quick function to check if we’re running in the browser or not. This is useful since the window object won’t be accessible if this function gets called from the server, and calling our scrollTo function would throw an error.

Then we can call window.scrollTo() to scroll to the top.

You can call this function with an x and y co-ordinate:

// Can be called with x and y co-ords
window.scrollTo(x, y);

Or with an options object, which has this interface:

interface ScrollToOptions {
    left?: number;
    top?: number;
    behavior?: "auto" | "smooth";
}

Which can be used like this:

//Or pass in an object
window.scrollTo({top: 20, behavior: 'smooth'})

The benefit of using the second option is that you can enable smooth scrolling, so calling the function animates to the position given, instead of immediately snapping to it.

Then we just add in a button at the bottom of our page that will call our function:

function ScrollToTopButton() {
    return (
        <button className="absolute bottom-0 p-10" onClick={scrollToTop}>
            <FaArrowUp />
        </button>
    );
}

And done!

A Little Extra

The above example works perfectly fine, but let’s add a few more lines of code and add in some animations.

Firstly our current arrow is placed at the bottom of our page,, so whenever you reach the end you see it. Let’s change it to fixed so it’s always visible, and we’ll hide it before a certain point:

function ScrollToTopButton() {
    return (
        <button 
            className="fixed bottom-0 right-0 p-10" onClick={scrollToTop}>
            <FaArrowUp />
        </button>
    );
}

Then with some help from framer motion, we can add some animation. I’ll show you what it looks like, and then explain the code behind it.

Instead of just being placed at the bottom of our page, the button now shows up after a certain threshold, which I’ve arbitrarily set to half of the page height.
Now here’s the code behind it:

const ScrollToTopContainerVariants: Variants = {
    hide: { opacity: 0, y: 100 },
    show: { opacity: 1, y: 0 },
};

function ScrollToTopButton() {
    const { scrollYProgress } = useScroll();
    const controls = useAnimationControls();

    useEffect(() => {
        return scrollYProgress.on('change', (latestValue) => {
            if (latestValue > 0.5) {
                controls.start('show');
            } else {
                controls.start('hide');
            }
        });
    });

    return (
        <motion.button
            className="fixed bottom-0 right-0 p-10"
            variants={ScrollToTopContainerVariants}
            initial="hide"
            animate={controls}
            onClick={scrollToTop}>
            <FaArrowUp />
        </motion.button>
    );
}

Using Framer Motion, I’ve added in a scroll-triggered animation. Whenever we pass our scroll threshold, the animation switches between showing and hiding our arrow, and our arrow flies in and out. A subtle animation, but a great improvement for UX.

Conclusion

Thanks for reading! If this article helped you out, or if you’re having any issues, feel free to leave a comment below!

Avatar photo
👋 Hey, I'm Omari Thompson-Edwards
Hey, I'm Omari! I'm a full-stack developer from the UK. I'm currently looking for graduate and freelance software engineering roles, so if you liked this article, reach out on Twitter at @marile0n

💬 Leave a comment

Your email address will not be published. Required fields are marked *

We will never share your email with anyone else.