How to Create Dynamic Pages with Route Parameters and the useParams Hook in React
Tags: react, webdev, javascript, tutorial
Published: 2023-07-12 12:27:21

## **OUTLINE**

- Introduction
- Prerequisite
- Creating Dynamic Pages with Route Parameters and the useParams Hook in React
- Conclusion

---

## **Introduction**

In React, route parameters and the useParams hook are used to render dynamic pages based on the provided URL. Dynamic pages are web pages that change their content based on the route parameter. Route parameters are parts of the URL that can change and act as placeholders for dynamic values. 

For instance, in an e-commerce website, route parameters can be used to fetch and display specific product details on a product listing page.

The useParams hook is a built-in hook in React Router that allows us to easily get the values of route parameters inside a React component. This helps us display and fetch data dynamically based on the parameter values in the URL.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/poxsb0xxkestre7hyua1.gif)

In this article, we will explore how to create dynamic pages with route parameters and the useParams hook in React.

---

## **Prerequisite**

- HTML
- CSS
- JavaScript
- React

---

## **Creating Dynamic Pages with Route Parameters and the useParams Hook in React**

## **STEP 1: Setting up your React project**

For setting up a new React project, it would be helpful to follow the process described in the blog post [Setting your React project with Vite](https://dev.to/adetutu/setting-up-a-react-project-with-vite-a-step-by-step-guide-329k). In the new React project, remove the pre-existing code and styles in the `index.css`, `App.jsx`, and `App.css` files. 

Within the src directory, create a pages folder and add the following files along with their corresponding CSS files: `Home.jsx` and `Recipe.jsx` and also create the `RecipeList.jsx` and `RecipeList.css` files within the components folder. 

```react
data/
│  db.json/
src/
│  components/
     │  RecipeList/
          RecipeList.jsx
          RecipeList.css
│  hooks/
     │  useFetch.jsx
│  pages/
     │ Home/
          Home.jsx
          Home.css
     │  Recipe/
          Recipe.jsx
          Recipe.css
│  App.css/
│  App.jsx/
│  index.css/
│  main.jsx/
index.html/

```

The above file structure demonstrates how you can structure your code for this project with separate directories for data, components, hooks, and pages.

---

## **STEP 2: Setting up a JSON server**

We would be using the JSON Server in this project because it allows us to generate API endpoints using a JSON file as a database. This allows us to integrate this API into our front-end project. Follow the steps outlined below to setup the JSON server for your project:

- Install JSON server

```react
npm install -g json-server
```

- Create a new folder named `data` and a new file in your project directory called `db.json` which will act as the JSON database.

```react
data/
│  db.json/
```

- Open the db.json file in a text editor and define your JSON data structure.

```react
{
    "recipes": [
    {
        "id": "1",
        "title": "Spaghetti Bolognese",
        "author": "John Smith",
        "ingredients": [
            "1 pound ground beef",
            "1 onion, chopped",
            "2 cloves garlic, minced",
            "1 can crushed tomatoes",
            "1/4 cup tomato paste",
            "1/2 cup beef broth",
            "1 teaspoon dried oregano",
            "1 teaspoon dried basil",
            "Salt and pepper to taste",
            "8 ounces spaghetti"
        ],
        "instructions": [
            "In a large skillet, cook ground beef over medium heat until browned.",
            "Add onion and garlic to the skillet and cook until softened.",
            "Stir in crushed tomatoes, tomato paste, beef broth, oregano, basil.",
            "Simmer the sauce for 20 minutes, stirring occasionally.",
            "Cook spaghetti according to package instructions.",
            "Serve the sauce over cooked spaghetti."
        ],
        "cookingTime": 30,
        "tags": ["pasta", "Italian", "dinner"]
    }
 ]
}

```

- In the terminal, run the following command to start the JSON server:

```react
json-server –watch ./data/db.json
```

The server will be running locally on port 3000, and you can access the data in the `db.json` file via the provided  API endpoints. 

```react
  json-server --watch ./data/db.json

  Loading ./data/db.json
  Done

  Resources
  http://localhost:3000/recipes

  Home
  http://localhost:3000

  Type s + enter at any time to create a snapshot of the database
  Watching...
```

- Once the JSON Server is running, you can send HTTP requests to the defined API endpoints to interact with the data stored in the JSON file.

```react
http://localhost:3000/recipes
```

---

## **STEP 3: Create the useFetch hook for data fetching**

In this project, we will use the useFetch hook to fetch data from the JSON server API. We would achieve this by passing the API as an argument into the useFetch hook and The hook will return the data, isLoading, and error states.

In order to create the useFetch Hook, you can follow the steps outlined in this article [How to Create a Custom useFetch Hook in React](https://dev.to/adetutu/how-to-create-a-custom-usefetch-hook-in-react-4p7).

---

## **STEP 4: Setup React Router in the App.jsx file**

Firstly, we install React Router in the project by using the command below:

```react
npm install react-router-dom
```

Then we import imports the required components from the react-router-dom library, including Route and Routes.

```react
import { Route, Routes } from "react-router-dom"
```

Next, we declare the App component and wrap the project components with the Router component.

```react
import './App.css' 
import { Route, Routes } from "react-router-dom"
import Home from './pages/Home/Home'
import Recipe from './pages/Recipe/Recipe'

function App() {

  return (
    <div className='App'> 
        <Routes>
            <Route path="/" element={<Home />} />
            <Route path="/recipes/:id" element={<Recipe />} />
        </Routes>
    </div>
  )
}

export default App
```

In the above code, the root route `("/")` renders the Home component while the `/receipes/:id `route renders the Recipe component. `"/recipes/:id"` represents the dynamic path and `:id` represents a route parameter.  

---

## **STEP 5: Create the Home component**

We import the necessary dependencies: `RecipeList` component, useFetch hook, and the CSS file for styling the Home component. Then we declare the home component.

```react
import RecipeList from '../../components/RecipeList/RecipeList'
import useFetch from '../../hooks/useFetch';
import './Home.css'

function Home() {

const { data, isLoading, error } = useFetch('http://localhost:3000/recipes')

  return (
    <div className='home-container'>
      <h1 className="home-title">Recipe List</h1>  
       {error && <p className='error'>{error}</p>}
       {isLoading && <p className='loading'>Loading Recipies...</p>}
      <RecipeList recipes={data}/>
    </div>
  )
}

export default Home

```

In the above code, we import the useFetch hook and use it to fetch data from the JSON server API. We pass in the API endpoint of the JSON server into the useFetch Hook and get the following states `data`, `isLoading`, and `error` in return using object destructuring.

We also import and render the `RecipeList` component and data is passed as the recipes prop into this component.

We display a loading message while the data is being fetched and an error message if an error occurs. Once the data is fetched successfully, it renders the `RecipeList` component to display the list of the recipes.

---

## **STEP 6: Add the CSS styles to the Home Component**

```css
.home-container {
    max-width: 1200px;
    margin: 30px auto;
    text-align: center;
}

.home-title{
    font-size: 50px;
}
```

---

## **STEP 7: Create the RecipeList component**

Firstly, we import the Link component from the react-router-dom library and the CSS file for styling the `RecipeList` component, then we declare the `RecipeList` component.

```react
import './RecipeList.css' 
import { Link } from 'react-router-dom'
  
function RecipeList({ recipes }) {

  if (!recipes) {
    return <p>No recipes available.</p>;
  }
  
  return (

    <div className='recipe-list'> 
      {recipes.map(recipe => (
        <div key={recipe.id} className='card'>  
            <h3 className='recipelist-title'>{recipe.title}</h3>
            <h4 className='recipelist-subtitle'>By {recipe.author}</h4>
            <p className='recipe-cooking-time'>Cook Time - {recipe.cookingTime} Minutes</p>
            <Link to={`/recipes/${recipe.id}`}>Cook Now</Link>
        </div> 
      ))}
    </div>
  )
}

export default RecipeList

```

In the above code, the `RecipeList` accepts a prop named recipes that contains an array of recipe objects. We check if the recipes prop is falsy (e.g., null or undefined). If there are no recipes available, it returns a paragraph element displaying the message "No recipes available."

Next, we map over the recipes array using the map function. For each recipe in the array, a div with a className of 'card' is rendered. It has a key attribute set to the id of the recipe object.

Lastly we declare the Link component which is used to create links to other routes within the application and it is set to `/recipes/${recipe.id}`. We use this to navigate to a specific recipe page based on the recipe's ID. We use `recipe.id` as a route parameter to fetch and display the details of the specific recipe.

---

## **STEP 8: Add the CSS styles to the RecipeList Component**

```react
.recipe-list {
    display: grid;
    grid-template-columns: 1fr 1fr 1fr;
    grid-gap: 40px;
    max-width: 1200px;
    margin: 50px 20px;
    text-align: center;
  }

  .recipe-list .card {
    box-shadow: 4px 4px 6px rgba(0,0,0,0.05);
    background: #fff;
    padding: 50px 0;
    border-radius: 8px;
  }

  .recipelist-title {  
    margin-bottom: 4px;
    font-size: 26px;
  }

  .recipelist-subtitle {
    color: #555;
    font-size: 18px;
  }

  .recipe-cooking-time {
    color: #999;
    font-size: 16px;
    margin-bottom: 30px;
  }

  .recipe-list .card a {
    color: #555;
    text-decoration: none;
    background: #f4f0ec;
    font-size: 20px;
    text-align: center;
    padding: 12px 20px;
    border-radius: 4px;
  }

  @media screen and (max-width: 900px) { 
    .recipe-list {
      grid-template-columns: 1fr 1fr;
    }
  }

  @media screen and (max-width: 650px) { 
  .recipe-list {
    grid-template-columns: 1fr;
  }

}

```

---

## **STEP 9: Create the Recipe component**

We import the `useParams` hook, `Link` component from the react-router-dom library, `useFetch` hook, and the CSS file for styling the Recipe component. 

```react
import { useParams, Link} from 'react-router-dom'
import useFetch from '../../hooks/useFetch';
import './Recipe.css'

export default function Recipe() {
  const { id } = useParams();
  const url = `http://localhost:3000/recipes/${id}`;
  const { error, isLoading, data: recipe } = useFetch(url)

  return (
    <div className="recipe-container">

      {error && <p className="error">{error}</p>}
      {isLoading && <p className="loading">Loading Recipe...</p>}
 
      {recipe && (
        <div className='recipe'>
          <h2 className="recipe-title">{recipe.title}</h2>
          <h3 className='recipe-author'> Recipe By {recipe.author}</h3>


          <div className="ingredients">
              <h3 className='recipe-ingredients recipe-subtitle'>Ingredients</h3>
              <ul>
                {recipe.ingredients.map(ing => <li key={ing}>{ing}</li>)}
              </ul>
          </div>

          <div className="instructions">
              <h3 className='recipe-instructions recipe-subtitle'>Instructions</h3>
              <ul>
                {recipe.instructions.map(ing => <li key={ing}>{ing}</li>)}
              </ul>
          </div>

          
        </div>
      )}

        <Link to="/" >Return to main page</Link>
    </div>
  )
}

```

In the above code, we call and destructure the `useParams` hook to obtain the `id` parameter which is used to identify a particular resource. 

We fetch and display the details of a specific recipe based on the id provided in the URL. The `id` is used to construct the URL for fetching the recipe data and we also make use of the `useFetch` custom hook to fetch recipe data from that specific URL.

We display a loading message while the data is being fetched and an error message if an error occurs. If the recipe data is available, the recipe details are rendered.

---

## **STEP 10:Add the CSS styles to the Recipe Component**

```css
.recipe-container {
  max-width: 800px;
  margin: 40px auto;
  text-align: center;
  background: #fff;
  padding: 40px;
}

.recipe {
   padding: 30px 0;
}

.recipe-title {
  font-size: 40px;
}

.recipe-author {
  color: #555;
  font-size: 20px;
  padding: 15px 0;
}

.recipe-subtitle {
  font-size: 30px;
  padding-top: 20px;
}

.recipe li {
  margin-left: 30px;
  font-size: 20px;
  line-height: 1.9;
  text-align: left;
}
 
.recipe-container a {
  color: #000;
  text-decoration: none;
  background: #f4f0ec;
  font-size: 20px;
  text-align: center;
  padding: 12px 20px;
  border-radius: 4px;
}

@media screen and (max-width: 540px) { 

.recipe-container {
  max-width: 800px;
  margin: 40px auto;
  text-align: center;
  background: #fff;
  padding: 40px 30px;
}

.recipe-title {
  font-size: 35px;
}

.recipe-author {
  padding: 25px 0;
  font-size: 18px;
}

.recipe-subtitle {
  text-align: left;
}

.recipe li {
  margin-left: 0px;
  font-size: 16px;
}

}
```

---

## **NOTE:** 

To run this application on your desktop: 

- Open the project and in the terminal, run the following command to start the JSON server

```react
json-server --watch ./data/db.json
```
- Next, start the development server by running

```react
npm run dev
```

---

## **Link**

[GitHub Link](https://github.com/adetutudeborah/dynamic-recipe-pages)

---

## **Conclusion**

We can leverage route parameters and utilize the useParams hook in React to build dynamic web pages where content and functionality can be customized based on specific parameters in the URL.  

Instead of creating separate pages for each variation, dynamic pages allow us to reuse a single component or by updating its content based on the route parameters.

By utilizing route parameters, we can pass variable values in the URL, such as the product IDs. These parameters can then be extracted using the useParams hook in React, which provides access to the parameter values within a component.