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
Now you click on a product to go to its detail page:
navigate('/products/123')
💥 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
).
✅ 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;
🧪 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>
);
};
✨ 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');
The hook will append the existing query from the current route:
/search?sort=desc&category=books&page=2
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)