DEV Community

Cover image for How to Create Dynamic Pages with Route Parameters and the useParams Hook in React
Adetutu Oluwasanmi
Adetutu Oluwasanmi

Posted on

How to Create Dynamic Pages with Route Parameters and the useParams Hook in React

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

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. 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.



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/



Enter fullscreen mode Exit fullscreen mode

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


npm install -g json-server


Enter fullscreen mode Exit fullscreen mode
  • Create a new folder named data and a new file in your project directory called db.json which will act as the JSON database.


data/
  db.json/


Enter fullscreen mode Exit fullscreen mode
  • Open the db.json file in a text editor and define your JSON data structure.


{
    "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"]
    }
 ]
}



Enter fullscreen mode Exit fullscreen mode
  • In the terminal, run the following command to start the JSON server:


json-server watch ./data/db.json


Enter fullscreen mode Exit fullscreen mode

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.



  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...


Enter fullscreen mode Exit fullscreen mode
  • 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.


http://localhost:3000/recipes


Enter fullscreen mode Exit fullscreen mode

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.


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

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



npm install react-router-dom


Enter fullscreen mode Exit fullscreen mode

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



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


Enter fullscreen mode Exit fullscreen mode

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



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


Enter fullscreen mode Exit fullscreen mode

In the above code, the root route ("/") renders the Home component while the /receipes/:idroute 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.



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



Enter fullscreen mode Exit fullscreen mode

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



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

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


Enter fullscreen mode Exit fullscreen mode

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.



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



Enter fullscreen mode Exit fullscreen mode

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



.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;
  }

}



Enter fullscreen mode Exit fullscreen mode

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.



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>
  )
}



Enter fullscreen mode Exit fullscreen mode

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



.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;
}

}


Enter fullscreen mode Exit fullscreen mode

NOTE:

To run this application on your desktop:

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


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


Enter fullscreen mode Exit fullscreen mode
  • Next, start the development server by running


npm run dev


Enter fullscreen mode Exit fullscreen mode

Link

GitHub Link


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.

Top comments (2)

Collapse
 
thecodeinn profile image
Richard Emijere

👑👑👑

Collapse
 
adetutu profile image
Adetutu Oluwasanmi

Thank you!