DEV Community

Vineeth.TR
Vineeth.TR

Posted on

Create a Custom React Hook to Persist URL Query Params with `react-router-dom`

When working with query parameters in your React app, especially in tools like filters, pagination, or search inputs, you often want to persist those query parameters as users navigate across different views.

In this post, you'll learn how to build a simple and reusable custom React hook using react-router-dom that preserves query parameters during route changes.


🛠 The Problem

Imagine you're on a URL like:

/products?category=books&page=2
Enter fullscreen mode Exit fullscreen mode

Now you click on a product to go to its detail page:

navigate('/products/123')
Enter fullscreen mode Exit fullscreen mode

💥 Boom! You lose your query params. When the user clicks the back button, they're dropped on /products instead of where they left off (/products?category=books&page=2).

lost


✅ The Solution

Let’s fix that by creating a useNavigateWithQuery hook.

This hook wraps React Router’s useNavigate and useLocation to preserve the existing query params from the current route.

// useNavigateWithQuery.ts
import { NavigateOptions, useLocation, useNavigate } from 'react-router-dom';

/**
 * A custom hook that wraps the `useNavigate` hook from `react-router-dom`.
 * It preserves the current query parameters from the URL when navigating to a new route.
 * If the target URL already includes query parameters, it appends the existing ones.
 */
const useNavigateWithQuery = () => {
    const navigate = useNavigate();
    const location = useLocation();

    return (to: string, options: NavigateOptions = {}) => {
        const query = location.search;
        const separator = to.includes('?') ? '&' : '?';
        navigate(`${to}${query ? separator + query.slice(1) : ''}`, options);
    };
};

export default useNavigateWithQuery;
Enter fullscreen mode Exit fullscreen mode

🧪 How to Use It

Now, instead of using useNavigate directly, import and use your new hook:

import useNavigateWithQuery from './hooks/useNavigateWithQuery';

const ProductList = () => {
    const navigateWithQuery = useNavigateWithQuery();

    const handleProductClick = (productId: string) => {
        navigateWithQuery(`/products/${productId}`);
    };

    return (
        <ul>
            <li onClick={() => handleProductClick('123')}>View Product 123</li>
        </ul>
    );
};
Enter fullscreen mode Exit fullscreen mode

✨ Done! Now if your current page has query params, they'll be automatically carried over to the next route.


🔍 Bonus: Handling Explicit Query Params

Let’s say you’re navigating to a URL that already has its own query params:

navigateWithQuery('/search?sort=desc');
Enter fullscreen mode Exit fullscreen mode

The hook will append the existing query from the current route:

/search?sort=desc&category=books&page=2
Enter fullscreen mode Exit fullscreen mode

It intelligently handles adding ? or & based on whether the to route already includes query parameters.


🚀 Wrap Up

This small but mighty hook helps preserve UX context when navigating across routes in your app. It’s especially useful when building dashboards, admin panels, or anything with filters and pagination.

Let me know if you’d like to see a version that merges or filters specific query params!

Happy coding! 💻

Top comments (0)