Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 76

Dynamic Product Routes

Recommended resources

Hello! Welcome back to our e-commerce project.

In our last session, we significantly improved the product catalog by adding a category filter. By chaining array methods and managing state, you built a feature that allows users to seamlessly search and filter products. This has made our main catalog page quite dynamic.

Today, we'll build on that by allowing users to click on a product to view its dedicated page. This lesson addresses the learning outcome: Create a dynamic route for a product detail page that uses a product ID from the URL. We'll use React Router to handle this, which is a fundamental skill for building any Single Page Application (SPA).

This concept is likely familiar to you from your Laravel experience, where you might define a route like Route::get('/products/{id}', ...) to display a specific product. In React, we'll achieve a similar outcome on the client side.

1. Understanding Dynamic Routes

A "dynamic route" is a route that can match multiple different URL paths by using a variable part, often called a route parameter. For our app, we want URLs like /products/1, /products/2, and /products/3 to all render the same ProductDetailPage component, which will then be responsible for fetching and displaying the data for the product with the corresponding ID.

To get a clear overview of this concept, let's watch the introduction of this helpful video.

Full React Tutorial #25 - Route Parameters

The Net Ninja provides a great explanation of what route parameters are and why they are useful in a real-world application like a blog.

Watch the first minute of the video to understand the concept of dynamic routes and route parameters.


2. Setting Up the Product Detail Page

First things first, we need a component to render when a user navigates to a product detail URL.

  1. Create a new file in your project: src/pages/ProductDetailPage.jsx.
  2. Inside this new file, create a simple functional component as a placeholder. For now, it will just confirm that the correct component is being rendered.
// src/pages/ProductDetailPage.jsx

import React from 'react';

const ProductDetailPage = () => {
  return (
    <div>
      <h2>Product Detail Page</h2>
    </div>
  );
};

export default ProductDetailPage;

3. Defining the Dynamic Route in the Router

Now, we need to tell React Router that any URL matching the pattern /products/:id should render our new ProductDetailPage component. The colon (:) prefix in the path is what marks a segment of the URL as a dynamic parameter.

  1. Open your main router configuration file (likely src/App.jsx or a similar top-level file where you've defined your <Routes>).
  2. Import the ProductDetailPage component.
  3. Add a new <Route> component inside your <Routes> block. We'll name our parameter productId.
// In src/App.jsx or your main router file

import { Routes, Route } from 'react-router-dom';
import ProductsPage from './pages/ProductsPage';
import ProductDetailPage from './pages/ProductDetailPage'; // <-- Import the new page
// ... other imports

function App() {
  return (
    // ... other components like Navbar might be here
    <Routes>
      <Route path="/" element={<ProductsPage />} />
      <Route path="/products" element={<ProductsPage />} />
      <Route path="/products/:productId" element={<ProductDetailPage />} /> {/* <-- Add this route */}
      {/* You might have a 404 route here as well */}
    </Routes>
  );
}

export default App;

This configuration tells React Router to match any path that starts with /products/ followed by some value, and to treat that value as a parameter named productId.


4. Linking to the Detail Page

With the route defined, we now need to make our product cards on the ProductsPage link to their respective detail pages. We will modify the ProductCard component to wrap its content in a <Link> component from react-router-dom.

  1. Open src/components/ProductCard.jsx.
  2. Import the Link component: import { Link } from 'react-router-dom';.
  3. The ProductCard component receives a product object as a prop, which contains the id. We'll use this id to construct the URL dynamically.

Here's how you can modify your ProductCard component. We'll use an ES6 template literal to build the to prop's value.

// src/components/ProductCard.jsx

import React from 'react';
import { Link } from 'react-router-dom';

const ProductCard = ({ product }) => {
  return (
    // The `to` prop uses a template literal to construct the dynamic URL
    <Link to={`/products/${product.id}`} style={{ textDecoration: 'none', color: 'inherit' }}>
      <div style={{ border: '1px solid #ccc', padding: '1rem', borderRadius: '8px', height: '100%' }}>
        <img src={product.image} alt={product.title} style={{ width: '100%', height: '150px', objectFit: 'contain' }} />
        <h3 style={{ fontSize: '1rem', margin: '0.5rem 0' }}>{product.title}</h3>
        <p style={{ fontSize: '1.25rem', fontWeight: 'bold' }}>${product.price}</p>
      </div>
    </Link>
  );
};

export default ProductCard;

Now, run your application. Navigate to the products page, and you should find that each product card is a clickable link. Clicking on a card should navigate you to a URL like /products/1, and you should see the "Product Detail Page" heading from our placeholder component.


5. Accessing the Route Parameter

The final step is to access the productId from the URL within the ProductDetailPage component. This will allow us to eventually fetch the data for that specific product. React Router provides a convenient hook for this: useParams.

The useParams hook returns an object containing key-value pairs of the URL parameters. The key will match the name you gave the parameter in the <Route> definition (productId in our case).

Let's update ProductDetailPage.jsx to use this hook.

// src/pages/ProductDetailPage.jsx

import React from 'react';
import { useParams } from 'react-router-dom'; // 1. Import useParams

const ProductDetailPage = () => {
  // 2. Call the hook to get the params object
  const params = useParams();
  
  // 3. Destructure the productId from the params object
  const { productId } = params;

  return (
    <div>
      <h2>Product Detail Page</h2>
      <p>Displaying details for product with ID: <strong>{productId}</strong></p>
    </div>
  );
};

export default ProductDetailPage;

Note: You can also destructure directly: const { productId } = useParams();

Watch this short clip to see a demonstration of using useParams and setting up the dynamic link, which mirrors the steps we just took.

Full React Tutorial #25 - Route Parameters

This part of the video demonstrates exactly what we just did: creating dynamic links and then using the useParams hook to capture the ID in the destination component.

Watch from 03:19 to 07:15. The first part covers useParams and the second part shows how to create the <Link> with a dynamic to prop, reinforcing our implementation.

For a quick-reference text-based example of this pattern, the following article is also very clear.

React Router Demystified: A Developer's Guide to Efficient Routing

The article 'React Router Demystified' has an excellent, self-contained section on useParams that shows the complete pattern: defining the route, linking to it, and using the hook.

Read the section titled 'useParams'. It provides a concise code example for setting up a product detail route and accessing the id parameter, which will reinforce what you've just built.

Now, when you navigate to /products/5, your detail page will display "Displaying details for product with ID: 5". We have successfully created the dynamic page and captured the identifier we need.


Conclusion

Great job! You have successfully implemented one of the most common and essential routing patterns in a single-page application.

Key Takeaways:

  • Dynamic Route Definition: You defined a route with a parameter using the path="/path/:paramName" syntax in the <Route> component.
  • Dynamic Linking: You created navigation links to dynamic routes using the <Link> component and an ES6 template literal to construct the to prop (e.g., to={\/products/${product.id}`}`).
  • Accessing Parameters: You used the useParams hook from react-router-dom to easily access the value of a URL parameter inside a component.

Preview of the Next Lesson:

Our detail page is now correctly routed, but it's not showing any actual product data yet. In the next lesson, we will fetch and display detailed information for a single product on its page. We'll use the productId we just captured to make a new API call and render the complete details for the selected item.