Proper Usage of useParams()
in React Router
When working with dynamic route parameters in React Router, the useParams()
hook is essential but often misused during destructuring. Here’s how to handle it correctly.
The Core Problem
Common mistakes when using useParams()
:
// ❌ Incorrect - assumes param always exists
const { id } = useParams();
// ❌ Incorrect - type unsafe
const params = useParams();
const productId = params.productId; // Could be undefined
Correct Implementation
1. Basic Usage with Optional Chaining
function ProductPage() {
const params = useParams();
const productId = params.productId; // string | undefined
if (!productId) {
return <div>Product not found</div>;
}
return <ProductDetails id={productId} />;
}
2. Type-Safe Approach (TypeScript)
interface ProductParams {
productId: string;
}
function ProductPage() {
const { productId } = useParams<ProductParams>();
if (!productId) {
return <div>Product not found</div>;
}
return <ProductDetails id={productId} />;
}
Common Mistakes and Solutions
❌ Assuming Parameters Always Exist
function UserProfile() {
const { userId } = useParams(); // ❌ userId could be undefined
useEffect(() => {
fetchUser(userId); // ❌ Potential runtime error
}, [userId]);
return <div>User Profile</div>;
}
✅ Fix: Add existence check
function UserProfile() {
const { userId } = useParams();
useEffect(() => {
if (!userId) return;
fetchUser(userId); // ✅ Safe
}, [userId]);
if (!userId) return <div>User not found</div>;
return <div>User Profile</div>;
}
❌ Not Handling Optional Parameters
// Route: "/posts/:postId?"
function PostPage() {
const { postId } = useParams(); // ❌ May be undefined
return <div>{postId ? `Post ${postId}` : 'All Posts'}</div>;
}
✅ Fix: Explicitly handle optional case
function PostPage() {
const params = useParams();
const isSinglePost = !!params.postId;
return (
<div>
{isSinglePost ? (
<SinglePost id={params.postId!} />
) : (
<AllPosts />
)}
</div>
);
}
Advanced Patterns
1. Parameter Validation
function isValidId(id: string | undefined): id is string {
return !!id && /^[a-z0-9-]+$/.test(id);
}
function ProductPage() {
const { productId } = useParams();
if (!isValidId(productId)) {
return <NotFound />;
}
return <ProductDetails id={productId} />;
}
2. Multiple Parameters
interface CategoryParams {
category: string;
subcategory?: string;
}
function CategoryPage() {
const { category, subcategory } = useParams<CategoryParams>();
return (
<div>
<h1>{category}</h1>
{subcategory && <h2>{subcategory}</h2>}
</div>
);
}
3. With Query Parameters
function SearchPage() {
const { query } = useParams(); // From route: "/search/:query"
const [searchParams] = useSearchParams(); // From URL: "?sort=asc"
const sortOrder = searchParams.get('sort');
return <SearchResults query={query || ''} sort={sortOrder} />;
}
TypeScript Best Practices
1. Explicit Parameter Types
type RouteParams = {
projectId: string;
taskId?: string;
};
function TaskPage() {
const { projectId, taskId } = useParams<RouteParams>();
// projectId is string | undefined
// taskId is string | undefined
}
2. Non-Null Assertion (When Appropriate)
// Only use when route MUST have the parameter
function UserDetail() {
const { userId } = useParams() as { userId: string };
// Or
const { userId } = useParams<{ userId: string }>();
return <UserProfile id={userId} />;
}
Debugging Tips
- Log parameters to verify:
const params = useParams();
console.log('Route params:', params);
- Check your route definitions match the URL structure:
<Route path="/products/:productId" element={<ProductPage />} />
- Use React DevTools to inspect matched routes
- Verify parameter names match between route and usage
Key Takeaways
- Never assume parameters exist – always check
- Use TypeScript for type safety with parameters
- Handle optional parameters explicitly
- Validate parameters when they affect data fetching
- Combine with other hooks like
useSearchParams
when needed
Proper use of useParams()
ensures your dynamic routes work reliably and helps prevent runtime errors from undefined parameters. Always consider the edge cases where parameters might be missing or invalid.