How to Create Dynamic Pages with Route Parameters and the useParams Hook in React
Adetutu Oluwasanmi
Posted on July 12, 2023
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.
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/
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
- Create a new folder named
data
and a new file in your project directory calleddb.json
which will act as the JSON database.
data/
│ db.json/
- 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"]
}
]
}
- In the terminal, run the following command to start the JSON server:
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.
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.
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.
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
Then we import imports the required components from the react-router-dom library, including Route and Routes.
import { Route, Routes } from "react-router-dom"
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
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.
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
.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.
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
.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.
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
.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
json-server --watch ./data/db.json
- Next, start the development server by running
npm run dev
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.
Posted on July 12, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
November 8, 2024