DEV Community

Cover image for 5 Remarkable React Router Features (Anchor Links, Query Params & more)
Gedalya Krycer
Gedalya Krycer

Posted on

5 Remarkable React Router Features (Anchor Links, Query Params & more)

Overview

I love React Router and how it truly expands the use case for React. Today we will take a quick look at what it does and then dive into 5 features and tricks that I feel are pretty remarkable.

Route to a section:

Refresher

What is it?

React Router is a game-changing package that allows us to turn our React SPA (single page application), into a virtual "multi-page" experience.

Why do we need it?

Typically when we navigate to a new page, the client (browser) sends a request to the server and indicates that a particular route (page) needs to be accessed. (For example, the /about route would deliver the about.html file.)

However, React is anything but typical and operates only on the client-side. Therefore we can't request a new page from the server, because we only have access to the index.html file. So we need a way to at least mimic multiple pages in our SPAs.

What does it do?

React Router intercepts this request on the client-side and instead renders a new component that we specify.

For example, it would render a component called <AboutPage /> which in turn holds <Bio /> and <Skills /> children components that make up the content of that "page".

It looks like we went to a new "About" page, with a new URL slug* and content. In reality, we are still in our trusty index.html file with its content being rapidly replaced based on routing. 🤯

*The slug follows the main domain and explains what the page is: website.com/this-is-the-slug

Completly new to React Router? Check out the resource links at the bottom to get started on the basics first.

Back to 🔝


Remarkable Features & Tricks

1️⃣ Fixing Anchor Links

Ok, this is more of a trick than a feature, but you would think creating an anchor link should be simpler than it is.

A logical assumption would be that <Link to='/about#skills'> would take us to the <About /> component and auto-scroll to the "skills" section.

Sadly this doesn't work out of the box, but there is a simple add-on package that comes to the rescue.

  1. $ npm install --save react-router-hash-link

  2. Import the package into your component.

  3. <Link> now accepts a hash anchor URL 🎉

...
import { HashLink as Link } from 'react-router-hash-link';

<Link to='/about#skills'>Skills</Link>
Enter fullscreen mode Exit fullscreen mode

Back to 🔝


2️⃣ Building Relative Paths

There comes a time when we need to nest routes within other components. For example, linking to a blog post within a certain category.

Our target URL should look like: website.com/pets/catsvsdogs

The problem is that React Router will direct you to the main domain website.com/catsvsdogs and not append the post slug to the /pets/ category as shown above.

This is because by default it treats anything you are linking to as an Absolute path. What we really need is a path relative to the page you are on.

Hard-Code Solution 👎

Hard-coding a relative path is not recommended because if you ever change the parent(s) route further up the URL slug, the path would break.

// Hard-coding the category and post slugs
<Link to='/pets/catsvsdogs'>Cats vs Dogs</Link>
<Route path='/pets' component={blogPost}/>

// The above will break if we make this change to the route
<Route path='/animals' component={blogPost}/>
Enter fullscreen mode Exit fullscreen mode

Dynamic Solution (Hooks) 👍

A much better solution is to leverage the url property from the match object that each <Route /> has. This allows us to dynamically place the current route's URL within the <Link />. and <Route />.

The useRouteMatch() hook lets us destructure the url and the path properties from the match object.

The path property is similar to the url, but is used with <Routes />.

To make everything truly dynamic, let's also convert the above code into a map that generates a postId as part of the <Link> URL parameters. Then we will set up the <Route /> to accept any id, by adding /:postId at the end of its path.

import { Route, Link, useRouteMatch } from 'react-router-dom';

// Custom Hook
const {path, url} = useRouteMatch();

// Dynamic list of blog post links.
<ul>
  {postsArray.map(({id, name}) => (
   <li key={id}>
     <Link to={`${url}/${id}`}>{name}</Link>
   </li>
  ))}
</ul>

// Route uses the path property to tell which URL it should match and accepts a dynamic id
<Route path={`${path}/:postId`} component={blogPost}/>
Enter fullscreen mode Exit fullscreen mode

Dynamic Solution (Classes) 👍

With class-based components, we can essentially take the same process as above. Instead of using a hook, we access the url in the match object via props.

import { Route, Link } from 'react-router-dom';

// Dynamic list of blog post links.
<ul>
  {postsArray.map(({id, name}) => (
   <li key={id}>
     <Link to={`${this.props.match.url}/${id}`}>{name}</Link>
   </li>
  ))}
</ul>

// Route uses props to get the matching url and accepts a dynamic id
<Route path={`${this.props.match.url}/:postId`} component={blogPost}/>
Enter fullscreen mode Exit fullscreen mode

Back to 🔝


3️⃣ Passing Props via withRouter()

Routes come with a robust set of information that is delivered in the form of props. For example, we can extract params we had set up or a different location to navigate to. (Prop data is stored in location, match, and history objects.)

Often times in our projects we have a component that is not associated with a route but could benefit from the above props. We could pop-drill the data we want, but that could get confusing and messy quickly.

Instead, we can use the higher-order component withRouter() on the component that just needs quick access to the props. For example, a form that wants to history.push() to a location such as a confirmation screen.

•••
import { withRouter } from 'react-router';

const Form = () => {

  // This function uses the route props to go to a new page after handling the form submission
  const handleSubmit = (event) => {
    •••
    props.history.push(`/confirmation`)
  };

  <form onSubmit={handleSubmit}>
    •••
  </form>

}

// Higher-order component that exposes the closest route's props to the Form component
export default withRouter(Form) 
Enter fullscreen mode Exit fullscreen mode

Back to 🔝


4️⃣ Passing Data via URL Query Params

React Router lets us pass data through the URL so that it can be consumed by the linked-to component. Appending this data to the URL is called query parameters.

useLocation() & custom extraction hook

import {
  •••
  Link,
  useLocation
} from 'react-router-dom';

// React Router suggests this custom hook to pull the value from the url
const useQuery = () => {
  return new URLSearchParams(useLocation().search);
}

// Component that has the data we want to send
const ParentComponent = () => {

  // Hook from above
  let query = useQuery();

  return (
    <div>
      {/* Data is added after the "?" */}
      <Link to='/account?name=netflix'>Netflix</Link>

      {/* Data is pulled out of the URL and passed as a prop to the child component  */}
      <ChildComponent name={query.get('name')} />
    </div>
  );
}

// Component receiving query params props 
const ChildComponent = ({name}) => {
  return <h1>{name}</h1>
}
Enter fullscreen mode Exit fullscreen mode

useParams() Hook

import { useParams } from 'react-router-dom';

const Blog = () => {
  return (
    {/* Link passes in an id as params in the slug*/}
    <Link to={`${props.match.url}/${id}`} />Post Name</Link>

    {/* Route is set up to dynamically accept any id passed in the slug */}
    <Route path=`${props.match.url}/:id`>
      <BlogPost />
    </Route>
  )
}

const BlogPost = () => {

  {/* useParams pulls the id param out of the slug so it can be used */}
  let { id } = useParams();
  return <div>Now showing post {id}</div>;

}
Enter fullscreen mode Exit fullscreen mode

Back to 🔝


5️⃣ Styling Active Links

A simple upgrade to a site's UX is to show an active style in the navigation for whatever page is showing.

Alt Text

React Router makes this simple with the <NavLink /> component, which replaces the standard <Link />.

import { NavLink } from 'react-router-dom';

<NavLink to='/' exact>Home</NavLink>
<NavLink to='/about'>About</NavLink>
<NavLink to='/contact'>Contact</NavLink>

// Note that the "Home" link has the prop "exact". 
// This prevents it from activating unless it's clicked.
Enter fullscreen mode Exit fullscreen mode

This new component adds an .active CSS class to any link that has its page shown. We can then target that generated class with any style we prefer.

.nav__link:hover, 
.nav__link:active,
.nav__link.active { <--- React Router Generated
  color: green;
}
Enter fullscreen mode Exit fullscreen mode

If we don't want to use the class name .active we can even specify our own name. This just needs to be passed to the activeClassName prop.

import { NavLink } from 'react-router-dom';

<NavLink 
  to='/' 
  exact 
  activeClassName='nav__link--active' <-------
>Home</NavLink>
Enter fullscreen mode Exit fullscreen mode

Alternatively, we could use the activeStyle JS styles prop to update the component directly.

import { NavLink } from 'react-router-dom';

<NavLink 
  to='/' 
  exact 
  activeStyle={{
    color: 'green'
  }}
>Home</NavLink>

Enter fullscreen mode Exit fullscreen mode

Back to 🔝


Summary

Alright, my friends, that is it for today. I hope you learned something new about React Router that will help your next project.

If you are looking to dig deeper, check out the various resources below. Happy coding! 🤓


Resource Links


Thumbnail designed with Figma

Top comments (1)

Collapse
 
mindactuate profile image
Daniel Gruner

Hi you can use anchor links without an external dependency. Just see here dev.to/mindactuate/scroll-to-ancho...