TypeOfNaN

How to preserve query parameters in React Router links

Nick Scialli July 02, 2021🚀 3 minute read

react router

React Router makes front-end routing a breeze, but it’s not always obvious how to handle query parameters.

Let’s say you have a query parameter in your current route that represents a coupon on your commerce site:

https://www.mysite.com/some-route?coupon=abc123

Now, you have a link to the /checkout page:

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

function SomePage() {
  return (
    <>
      <p>Read to checkout?</p>
      <Link to="/checkout">Checkout</Link>
    </>
  );
}

Much to your chagrin, you realize that clicking this link erases the coupon code:

https://www.mysite.com/checkout

A one-time fix

Fortunately, React Router has a handy useLocation hook we can use to grab the query string. Here’s how it works:

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

function SomePage() {
  const { search } = useLocation();

  return (
    <>
      <p>Read to checkout?</p>
      <Link to={`/checkout${search}`}>Checkout</Link>
    </>
  );
}

And that will work!

But what if you need this pattern of retaining any search parameters in multiple places or even site-wide? It would be a pain to keep adding this hook and associated to prop interpolation to every place you need it.

Writing a custom LinkWithQuery component

We can achieve the desired abstraction by writing a custom component:

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

export const LinkWithQuery = ({ children, to, ...props }) => {
  const { search } = useLocation();

  return (
    <Link to={to + search} {...props}>
      {children}
    </Lonk>
  );
};

Note that this custom component, LinkWithQuery, takes the same arguments as a Link component. It then uses the useLocation hook to grab the search string. Finally, it concatenates to and search in the Link and additionally spreads other props you provide.

Now we can use this component instead of Link and be confident that any query parameters will be appended for us:

import { LinkWithQuery } from './LinkWithQuery';

function SomePage() {
  return (
    <>
      <p>Read to checkout?</p>
      <LinkWithQuery to="/checkout">Checkout</LinkWithQuery>
    </>
  );
}

If you'd like to support this blog by buying me a coffee I'd really appreciate it!

Nick Scialli

Nick Scialli is a software engineer at the U.S. Digital Service.