Why and How to Use URLs for State Management in React

When we get used to using useState, we often forget other ways to preserve the state in our applications.

A handy trick is to include state information in your URLs.

We can do this easily by pulling data from query parameters in our URL.

This simple guide will show you why and how to derive your application state from URLs in React.js.

So, first off, why should you bother?

Why Use State in URLs?

Easy Sharing and Bookmarking

If your app's state is in the URL, users can bookmark or share their current app state. Imagine sharing a search result page with all filters applied just by copying the URL.

You may have a tab, modal, or other useful feature selected that you would like to share. If the state comes from the URL, it's easy to share.

Keep User State History

When the state is stored in the URL, users returning to your app can start right where they left off without needing to redo their steps.

If you refresh your tab and the state is in the URL, you'll be exactly where you left off.

It's also great if a user reports an issue and can share the exact state they are in simply with a URL.

Better Navigation Experience

Users can navigate their browser history and see meaningful changes in the app's state, making the back and forward buttons more useful.

So, rather than resetting your state, you will be exactly where you were.

Simplify State Management

Let's face it: We usually have way too much state in our apps to manage! By abstracting some of our states into our URLs where possible, we can create a much simpler experience for developers and users.

Implementing State in URLs

In this example, I'll use Next.js, but all routers can grab parameters and use them in your app.

Here's a basic example:

"use client";

import Link from "next/link";
// useSearchParams allows us to grab the query string from the url
import { useSearchParams } from "next/navigation";

function TabPage() {
  const searchParams = useSearchParams();

  const tab = searchParams.get("tab");

  // URL Example: -> `/dashboard?tab=1`
  // `tab` will equal '1'

  // Function to render tab content dynamically based on the active tab
  const renderTabContent = () => {
    switch (tab) {
      case "1":
        return <p>This is Tab 1 content</p>;
      case "2":
        return <p>This is Tab 2 content</p>;
      case "3":
        return <p>This is Tab 3 content</p>;
      default:
        return <p>Please select a tab.</p>;
    }
  };

  return (
    <div>
      <h1>My dashboard</h1>
      <ul>
        <li>
          <Link href="/dashboard?tab=1">Tab 1</Link>
        </li>
        <li>
          <Link href="/dashboard?tab=2">Tab 2</Link>
        </li>
        <li>
          <Link href="/dashboard?tab=3">Tab 3</Link>
        </li>
      </ul>
      <div>{renderTabContent()}</div>
    </div>
  );
}

export default TabPage;

Here, we render dynamic content based on the selected tab and store the state in the URL. Seeing it in action will help make it feel a little less scary.

I hope this helps to give you some ideas about where you can start using query parameters in your applications to manage the state.

Avatar for Niall Maher

Written by Niall Maher

Founder of Codú - The web developer community! I've worked in nearly every corner of technology businesses: Lead Developer, Software Architect, Product Manager, CTO, and now happily a Founder.

Loading

Fetching comments

Hey! 👋

Got something to say?

or to leave a comment.