Smooth Scrolling Made Easy: How to Implement Vertical Scrolling in React Using react-router-hash-link

As a React developer, you‘ve likely faced the challenge of implementing smooth scrolling navigation in your apps. You want to provide an intuitive experience where clicking a link seamlessly scrolls the page to the corresponding section, but the default browser behavior can be jarring. And trying to implement it yourself can quickly get complex, especially when using a routing library like react-router.

Fortunately, there‘s a handy library called react-router-hash-link that makes it a breeze to add smooth scrolling to your React apps. In this post, we‘ll dive into what react-router-hash-link is, how to set it up, and all the details you need to start using it effectively in your projects.

The Challenges of Implementing Smooth Scrolling in React

First, let‘s look at why implementing smooth scrolling in React can be tricky. The main issue is that React, by design, wants to control the DOM and manage browser behavior itself. Normally this is a good thing – it‘s what enables React‘s declarative component-based model.

But when it comes to scrolling, React doesn‘t have a built-in way to customize the default jump-to-element behavior when navigating via hash links (links that look like /page#section and jump to a particular ID on the page). So developers often end up resorting to native browser APIs or manually calculating element offsets to control the scrolling.

This gets even more complicated when using client-side routing libraries like react-router. Since these libraries override the default link behavior to enable single-page-app style routing, you can‘t rely on the browser‘s default scrolling at all. React Router provides some scrolling components and control via its component, but it doesn‘t support smooth animations natively.

Introducing react-router-hash-link

This is where react-router-hash-link comes in. As described in its documentation, it "provides a solution to React Router‘s issue of not scrolling to #hash-fragments when using the component to navigate."

Essentially, react-router-hash-link extends React Router‘s component, replacing it with a custom component. When you use , the library automatically handles scrolling to the element with the matching hash fragment identifier when the link is clicked.

Even better, it performs this scrolling with a smooth scrolling animation that looks much nicer than a sudden jump. Plus it provides options to customize this animation, control the vertical offset, and more. But at its core, it maps links to element IDs and ensures the page smoothly scrolls to the right place.

Setting Up react-router-hash-link in Your React Project

Integrating react-router-hash-link into your React app is straightforward. First, make sure you have react-router-dom installed, since react-router-hash-link is meant to work in conjunction with React Router. If you haven‘t already, run:

npm install react-router-dom

Then install react-router-hash-link itself:

npm install react-router-hash-link 

With the library installed, import the component into any React components where you‘ll be adding hash links:

import { HashLink } from ‘react-router-hash-link‘;

Creating Smooth Scrolling Navigation with HashLink

Now you‘re ready to start replacing your regular elements with . The HashLink component acts just like a Link, but with a "smooth" prop that enables the scrolling animation.

Here‘s what a navigation menu built with HashLink might look like:

<nav>
  <HashLink smooth to="/#about">About</HashLink>
  <HashLink smooth to="/#services">Services</HashLink>
  <HashLink smooth to="/#contact">Contact</HashLink>
</nav>

The "to" prop specifies the path and hash fragment to link to, while the "smooth" prop tells react-router-hash-link to animate the scroll.

Then, in the corresponding sections in your component, add matching element IDs:

<div>
  <section id="about">
    <h2>About Us</h2>
    <p>Lorem ipsum dolor sit amet...</p>
  </section>

  <section id="services">
    <h2>Our Services</h2>
    <p>Lorem ipsum dolor sit amet...</p>  
  </section>

  <section id="contact">
    <h2>Contact Us</h2>
    <p>Lorem ipsum dolor sit amet...</p>
  </section>
</div>

With those IDs in place, clicking a HashLink will smoothly animate the scroll position until that element is at the top of the viewport.

Customizing the Scrolling Behavior

By default, react-router-hash-link uses a smooth scrolling animation lasting 800ms. But you can customize this by passing a number to the "smooth" prop, specifying the duration of the animation in milliseconds:

<HashLink smooth="500" to="/#contact">Contact</HashLink>

You can also control the vertical offset. For example, if you have a fixed header, you may want to stop the scroll 100px from the top of the element so the content isn‘t hidden behind the header. To do this, use the "scroll" prop and provide a function that takes the element to scroll to and returns the desired scroll position:

<HashLink 
  to="/#services"
  scroll={el => el.offsetTop - 100}
>
  Services
</HashLink>

Accessibility Tips

When implementing smooth scrolling, it‘s important to keep accessibility in mind. Some users may have vestibular disorders or motion sensitivities that make animations uncomfortable. Others may use keyboard navigation or screen readers.

To address this, consider providing a way for users to disable animations if needed. One approach is to add a toggle that sets a prefers-reduced-motion media query on the document or root element. Then in your CSS, you can specify:

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }
}

This will override the smooth scrolling and revert to jump-to-element behavior for users who have expressed a preference for reduced motion.

Also make sure the elements you‘re linking to can receive focus and are accessible via keyboard navigation. If an element isn‘t usually focusable (like a

), you can add a tabindex attribute to make it focusable:

<div id="about" tabindex="-1">
  <!-- about section content -->
</div>

Putting It All Together: A Complete Example

Here‘s what a complete one-page React app might look like with react-router-hash-link implemented for smooth scrolling navigation:

import React from ‘react‘;
import { HashLink } from ‘react-router-hash-link‘;

function App() {
  return (
    <>
      <nav>
        <HashLink smooth to="/#about">About</HashLink>
        <HashLink 
          smooth 
          to="/#services"
          scroll={el => el.offsetTop - 100}
        >
          Services
        </HashLink>
        <HashLink smooth to="/#contact">Contact</HashLink>
      </nav>

      <main>
        <section id="about" tabindex="-1">
          <h2>About Us</h2>
          <p>
            Lorem ipsum dolor sit amet, consectetur adipiscing elit. 
            Duis aliquam est vel lectus iaculis, et viverra velit hendrerit. 
            Aliquam rhoncus neque a nisi tristique, id commodo nunc rutrum.
          </p>
        </section>

        <section id="services" tabindex="-1">
          <h2>Our Services</h2>
          <p>
            Lorem ipsum dolor sit amet, consectetur adipiscing elit. 
            Duis aliquam est vel lectus iaculis, et viverra velit hendrerit. 
            Aliquam rhoncus neque a nisi tristique, id commodo nunc rutrum.  
          </p>
        </section>

        <section id="contact" tabindex="-1">
          <h2>Contact Us</h2>
          <p>
            Lorem ipsum dolor sit amet, consectetur adipiscing elit. 
            Duis aliquam est vel lectus iaculis, et viverra velit hendrerit.
            Aliquam rhoncus neque a nisi tristique, id commodo nunc rutrum.
          </p>
        </section>
      </main>
    </>
  );
}

export default App;

Alternative Approaches

While react-router-hash-link is a great all-in-one solution for adding smooth scrolling to React Router apps, there are other ways to approach scrolling in React.

For simpler scrolling needs that don‘t require working with a router, you can use the native Element.scrollIntoView() method. Call this on a DOM element, and the browser will scroll that element into view:

const el = document.getElementById(‘services‘);
el.scrollIntoView({ behavior: ‘smooth‘ });

There are also other general-purpose smooth scrolling libraries, like locomotive-scroll, that aren‘t tied specifically to React or routing.

Ultimately, the approach you choose depends on your app‘s specific needs and architecture. But for many common use cases, especially with React Router, react-router-hash-link is an excellent choice.

Conclusion

By taking advantage of the react-router-hash-link library, you can add smooth scrolling to your React app with minimal hassle. This provides a much nicer user experience when navigating between sections within a page.

In this post, we‘ve covered what react-router-hash-link is, how to install it, how to replace your components with , and some of the ways you can customize the scrolling behavior. We also discussed some accessibility considerations and looked at a complete code example.

Animations and seamless navigation are the types of polish that can really elevate your React app‘s user experience. With the tips and examples provided here, you should be well on your way to implementing smooth scrolling in your own projects. Happy coding!

Similar Posts