A Quick Start Guide to Query Strings with React Router

I’ve been working on a project where we wanted to implement a set of filters for a list of records on one page of our React web app and store the selected filters as state in the URL. We chose this approach so we could link directly to the page with specific filters selected. Adding the filter values to the query string in the URL offered a reasonable solution.

If you are a web developer, chances are that you’ve worked with query strings before. Handling them in React Router is similar to other framework tooling and is simple to learn for query string first-timers. If you are new to React Router or want a refresher on how to retrieve and parse query strings, this guide is for you.

Query Strings 101

Query strings are commonly used for embedding parameters into a URL. A typical query string might look like this:

https://search.com/?q=react&ia=web

The query string follows the ?, and multiple parameters are separated by a delimiter–in this case an &. These are typically key-value pairs, but it is possible to embed complex objects or arrays into the URL in the query string. The query string can contain any useful information, such as search strings, filter values, or any transient data that needs to be shared across pages.

Query Strings in React Router

React Router gives developers the freedom to do whatever makes sense for their application. It has moved away from including built-in query string parsing; instead of trying to satisfy everyone’s needs, it lets users handle their own implementation. The good news is that it is pretty straightforward.

1. Getting started

Let’s say you have a component that shows a searchable list of products, with an optional filter for items on clearance. The route for this page looks like: https://shopping.com/products/. To include the search string and filter value in the URL, we want to have a query string that looks something like this:

https://shopping.com/products?q="shoes"&clearance=true

The route definition will look like this:

<Route exact path={"/products"} component={ProductsRouteLoader} />

And, using the loader pattern, the component could look like this:

import { asyncComponent } from "client/async-component";
import * as React from "react";
import { RouteComponentProps } from "react-router-dom";

export const ProductsRouteLoader = asyncComponent({
  resolve: async () => {
    const Search = (await import(
      "client/pages/products"
    )).Products;

    const Wrapper: React.FunctionComponent<
      RouteComponentProps
    > = props => {
      // Access query params here 
      // and pass them down to rendered component
      return (
       ...
      );
    };

    return Wrapper;
  },
  name: "ProductsPage",
});

2. Access router params

Next, we need to access the params stored in the URL.

React Router passes along a few objects in props: history, match, and location. The one we care about is location because it contains the information about the query params.

Here is what the object looks like:

location: {pathname: "/products", search: "?q="shoes"&clearance=true", hash: "", state: undefined, key: "eltjl1"}

Now we know we can get the query string via props.location.search, but we can’t exactly do anything with it yet. So, the next step is parsing.

3. Choose a method

Method 1: DIY with browser API

Choose this method if…

  • You’re targeting modern browsers that support the URL API. Check here
  • You prefer a lighter-weight browser implementation rather than an external library.
  • You need to handle parameters uniquely.
  • You want to leverage TypeScript.

For most modern browsers, you’ll have access to the URL API, meaning you can handle parsing yourself using URLSearchParams. Once you have a new URLSearchParams object, there are many methods available to work with the query string of your URL. In most cases, all you’ll need is to call the get() method with the key of the parameter you wish to access.

Rather than simply passing in a raw string key value, you can leverage types for added safety. The types for this route might look like this:

export namespace ProductsRoute {
  export enum QueryParamKeys {
    q = "q",
    clearance = "clearance",
  }

  export type QueryParams = {
    q: string;
    clearance: boolean;
  };

  const path = `${PATH_TEMPLATE}?${QueryParamKeys.q}=${q}&${QueryParamKeys.clearance}=${clearance}`;

  return generatePath(path)
}

We can pass ProductsRoute.QueryParamKeys.q or ProductsRoute.QueryParamKeys.clearance into the parse function and be assured that the keys are valid.

import { asyncComponent } from "client/async-component";
import * as React from "react";
import { RouteComponentProps } from "react-router-dom";

export const ProductsRouteLoader = asyncComponent({
  resolve: async () => {
    const Search = (await import(
      "client/pages/products"
    )).Products;

    const Wrapper: React.FunctionComponent<
      RouteComponentProps
    > = props => {
     const searchTerm = query.get(
        ProductsRoute.QueryParamKeys.q
      );
     const clearance = stringToBool(query.get(
        ProductsRoute.QueryParamKeys.clearance
      ));
 
      
      return (
       ...
      );
    };

    return Wrapper;
  },
  name: "ProductsPage",
});

Gotcha: When handling Boolean params, the get() method will return the string value `true` or `false`, which are both truthy. A simple helper function can make it much easier to correctly handle string Boolean values.

Method 2: Let a third-party library handle it

Choose this method if….

  • You’re targeting browsers that do not support the URL API, such as IE. Check here
  • No special handling is needed.

React Router recommends using a library such as query-string, which is available on npm if you are unable to use the built-in browser implementation of the URL API. Run yarn add query-string to add and install it. Parsing query strings is then as simple as passing location.search into the parse() function.

import { asyncComponent } from "client/async-component";
import * as React from "react";
import { RouteComponentProps } from "react-router-dom";
import * as QueryString from "query-string"

export const ProductsRouteLoader = asyncComponent({
  resolve: async () => {
    const Search = (await import(
      "client/pages/products"
    )).Products;

    const Wrapper: React.FunctionComponent<
      RouteComponentProps
    > = props => {
      const params = QueryString.parse(props.location.search);
      console.log(params.q); // "shoes"
      console.log(params.clearance); // true
      
      return (
       ...
      );
    };

    return Wrapper;
  },
  name: "ProductsPage",
});

Conclusion

Query strings add seamless functionality and usability to web applications by passing data between pages via the URL. With React Router, parsing these query strings is made simple with third-party libraries or using the browser-based implementation of the URL API.