Thursday, June 12, 2025

Data Loader With Dynamic Routes in React

In the post Data Loader in React Router we saw how to use loader function to fetch data for route component. In this post we'll see how to use data loader with dynamic routes in React.

Data Loading React Router Example

In this example we'll use a loader function to fetch data from API. For API call https://jsonplaceholder.typicode.com/posts resource is used which is a free fake API. In the example Bootstrap 5 is used for styling.

Route Configuration

src\components\routes\Route.js

import { createBrowserRouter } from "react-router";
import Home from "./home";
import NavigationNavLink from "./NavigationNavLink";
import ErrorPage from "./ErrorPage";
import PostList, {loader as PostLoader} from "./PostList";
import PostDetails, {loader as PostDetailsLoader} from "./PostDetails";
import PostLayout from "./PostLayout";

export const route = createBrowserRouter([
  {path: "/", element: <NavigationNavLink />, errorElement: <ErrorPage />,
   children: [
    {index: true, element: <Home /> },
    {path: "post", element: <PostLayout />,
     children:[
        {index: true, element: <PostList />, loader: PostLoader, hydrateFallbackElement: <h2>Loading...</h2>},
        {path: ":postId", element: <PostDetails />, loader: PostDetailsLoader, hydrateFallbackElement: <h2>Loading...</h2>},
     ]
    },
   ]
  },
])

As you can see there is a Navigation Menu component that is mapped to root route. Nested routes of root route are Home and PostLayout.

PostLayout again has nested routes for PostList to show all the posts and another as a dynamic route for showing details of a particular post by adding postId as path parameter to the URL.

Add this route configuration to RouteProvider.

<RouterProvider router={route}></RouterProvider>

Components used

Navigation Menu, Home and ErrorPage components remain as shown in this post- Data Loader in React Router.

src\components\routes\PostLayout.js

import { Outlet } from "react-router";
const PostLayout = () => {
  return(
    <div className="mx-2">
      <Outlet />
    </div>
  )
}

export default PostLayout;

There is just a <Outlet /> in this component to render the content for the child route components.

src\components\routes\PostList.js

import { Link, useLoaderData } from "react-router";

const PostList = () => {
  const postData = useLoaderData();
  return(
    <>            
      <h2 className="text-info-emphasis text-center">Posts</h2>
      <ul className="list-group">
        {postData.map((post) =>
        <li className="list-group-item" key={post.id}>
            {post.id} <Link to={post.id.toString()}>{post.title}</Link>  
        </li>
        )}
      </ul>
    </>
  )
}

export default PostList


export async function loader(){
  const response = await fetch('https://jsonplaceholder.typicode.com/posts?_limit=10');
  // check for any error
  if(!response.ok){
    // use Response object
    throw new Response("Failed to load data", { status: response.status });
  }else{
    const responseData = await response.json();
    return responseData;
  }
}

Some of the points to note here-

  1. In the component, loader function named loader is written.
  2. Uses fetch API to get data from the given URL. Only 10 posts are fetched.
  3. If there is no error in fetching data (response is ok) then you can return data directly with React Router V7.
  4. Data fetched by the loader function is retrieved by using the useLoaderData() hook.
  5. Post data is then iterated to print id and title of each post. With each post a Link with its id is also created

src\components\routes\PostDetails.js

import { useLoaderData } from "react-router";

const PostDetails = () => {
  const postData = useLoaderData();
  return(
    <>
      <h2 className="text-center">{postData.title}</h2>
      <div>{postData.body}</div>
    </>
  )
}

export default PostDetails;

export async function loader({request, params}){
  const url = "https://jsonplaceholder.typicode.com/posts/"+params.postId
  const response = await fetch(url);
  if (!response.ok) {
    throw new Response('Error while fetching post data', {status:404});
  }
  else{
    const responseData = await response.json();
    return responseData;
  }
}

Some of the points to note here-

  1. In the component, loader function named loader is written which is used to fetch data by post Id.
  2. In loader function you can’t use useParams() hook to get the route parameters. Router passes an object to the loader function that has two properties- request and params.
  3. Using request you can access the request body, request URL etc. Using params you can access the route path parameters.
  4. Data fetched by the loader function is retrieved by using the useLoaderData() hook. In this case data is post data for a particular ID.
  5. Post title and post body are rendered by this component.

PostList Page

Data Loader With Dynamic Routes

Post Detail page

Loader in React Router

That's all for this topic Data Loader With Dynamic Routes in React. If you have any doubt or any suggestions to make please drop a comment. Thanks!


Related Topics

  1. Routing in React With Example
  2. Setting 404 Error Page With React Router
  3. useNavigate in React Router to Navigate Programmatically
  4. useSearchParams in React Router - Handling Query Parameters
  5. Search Filter Using useSearchParams in React Router

You may also like-

  1. React Declarative Approach
  2. React Virtual DOM
  3. Controlled and Uncontrolled Components in React
  4. JavaScript Array map() Method With Examples
  5. ArrayList in Java With Examples
  6. Count Number of Times Each Character Appears in a String Java Program
  7. Java Stream API Interview Questions And Answers
  8. What is Client Side Routing in Angular

Data Loader in React Router

In this post we'll see how to use data loader in React Router to provide data to route components.

Loader in React Router

Loader is a function that is used to fetch data before rendering a route component. That is different from the way useEffect() hook works which also provides a way to fetch data by making API calls. The useEffect() in react is called, after component has rendered, as a side effect.

With data loading in React Router, loader function is called for fetching data first then the route component is rendered. That ensures availability of the data when the component renders.

Data Loading React Router Example

In this example we'll use a loader function to fetch data from API. For API call https://jsonplaceholder.typicode.com/posts resource is used which is a free fake API. In the example Bootstrap 5 is used for styling.

Components used

There is a navigation menu.

src\components\routes\NavigationNavLink.js

import { NavLink, Outlet } from "react-router";
import "./navigation.css";
const NavigationNavLink = () => {
  const style = (({ isActive, isPending }) => 
      isPending ? "pending" : isActive ? "active" : ""
  );
  return(
    <>
      <nav id="menu" className="navbar navbar-expand-lg bg-dark navbar-dark">
        <div className="container-fluid">
          <button className="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarMenu" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
              <span className="navbar-toggler-icon"></span>
          </button>

          <div className="collapse navbar-collapse" id="navbarMenu">
            <ul className="navbar-nav">
              <li>
                  <NavLink className={style} to="/">
                      Home
                  </NavLink>
              </li>
              <li>
                  <NavLink className={style} to="/post">
                      Post
                  </NavLink>
              </li>
            </ul>
          </div>
        </div>
      </nav>
      <div className="container row mt-2">
         <Outlet />
      </div>
    </>
  );
}

export default NavigationNavLink;

CSS used for styling the active link.

src\components\routes\navigation.css

#menu a:link,
#menu a:visited {
    color: gray;
}
#menu a:hover {
    color: white;
}
#menu a.active {
    color: #7FFFD4;
}

#menu a {
    text-decoration: none;
}

#menu ul {
    gap: 1rem;
}

Home component

src\components\routes\Home.js

const Home = () => {
    return (
    <>
        <h2>This is home page</h2>
    </>
    )
}

export default Home;

PostList component

This is the component which shows the fetched posts, loader function is also written in the same component.

src\components\routes\PostList.js

import { useLoaderData } from "react-router";

const PostList = () => {
  const postData = useLoaderData();
  return(
    <div className="container">
      <h2 className="text-info-emphasis text-center">Posts</h2>
      <ul className="list-group">
        {postData.map((post) => 
        <li className="list-group-item" key={post.id}>
            {post.id} {post.title}
        </li>
        )}
      </ul>
    
    </div>
  )
}

export default PostList

export async function loader(){
  const response = await fetch('https://jsonplaceholder.typicode.com/posts?_limit=10');
  // check for any error
  if(!response.ok){
      // use Response object
      throw new Response("Failed to load data", { status: response.status });
  }else{
      const responseData = await response.json();
      return responseData;
  }
}

Some of the points to note here-

  1. In the component, loader function named loader is written.
  2. Uses fetch API to get data from the given URL. Only 10 posts are fetched.
  3. If there is no error in fetching data (response is ok) then you can return data directly with React Router V7. You don’t need to wrap in json() function like this.
    return json(await response.json());
    
  4. Other points are explained later in the post.

How to set loader in route

In the PostList component, loader function is defined but you have to let the router know that the specific loader function has to be called for the given route component. That is done using loader property in the router configuration.

src\components\routes\route.js

import { createBrowserRouter } from "react-router";
import Home from "./home";
import NavigationNavLink from "./NavigationNavLink";
import PostList, {loader as PostLoader} from "./PostList";

export const route = createBrowserRouter([
    {path: "/", element: <NavigationNavLink />,
     children: [
        {index: true, element: <Home /> },
        {path: "post", element: <PostList />, loader: PostLoader},
     ]
    },
])
As you can see loader function is imported as PostLoader
import PostList, {loader as PostLoader} from "./PostList";

and later provided in the route definition as a value of the loader property.

{path: "post", element: <PostList />, loader: PostLoader}

How to get loader data in components

Another question that arises is; how to access the data, fetched by the loader, in the component.

To access data in a component useLoaderData() hook is used which returns data from the closest route loader function.

That is how fetched post data is made available to the PostList component.

const postData = useLoaderData();

Which is then iterated to print id and title of each post.

Error handling in loaders

We should also take into consideration the scenario when loader is not able to fetch data because of some problem.

Couple of things we need to do here is to set the error message and status and also configure an error page to display the error.

When an error occurs during data fetching within a loader function, you can throw a Response object. With Response object you can include both status code and a message.

In the PostList component showed above Response object is already thrown.

if(!response.ok){
  // use Response object
  throw new Response("Failed to load data", { status: response.status });
}

Error page component

You can create a separate component for displaying errors. To get the error message and status you can use useRouteError() hook.

src\components\routes\ErrorPage.js

import { isRouteErrorResponse, useRouteError } from "react-router";

const ErrorPage = () => {
  const error = useRouteError();
  //console.log(error);
  if (isRouteErrorResponse(error)) {
    return (
      <>
        <h1>
          {error.status} {error.statusText}
        </h1>
        <p>{error.data}</p>
      </>
    );
  } else if (error instanceof Error) {
    return (
      <div>
        <h1>Error</h1>
        <p>{error.message}</p>
        <p>The stack trace is:</p>
        <pre>{error.stack}</pre>
      </div>
    );
  } else {
    return <h1>Unknown Error</h1>;
  }
}

export default ErrorPage;

You also need to set the Error page using the errorElement prop in the parent route. Even if an error occurs in the Child component, it will bubble up to the Root route where <ErrorPage /> component defined in the errorElement prop will catch that error.

{path: "/", element: <NavigationNavLink />, errorElement: <ErrorPage />,

For more details about setting error page, refer this post- Setting 404 Error Page With React Router

Loading state

While the data is fetched by the loader, if you want to show a loading message for better user experience then you can use useNavigation() hook to do that.

useNavigation hook provides access to the state of navigation, which are as following-

  1. idle: No navigation is happening.
  2. loading: A navigation is in progress.
  3. submitting: A form submission is in progress.

With ReactRouter V7 you should also provide hydratefallbackelement.

Using useNavigation hook to show loading state

If you are using useNavigation hook then it should be used one level above the current route. Which is logical because component is rendered only after fetching data when you are using data loading in routes. In our example navigation menu component is the suitable choice for showing loading state.

Changes needed in the NavigationNavLink component are to import useNavigation() hook and get the current navigation.

import { useNavigation } from "react-router";

then

const navigation = useNavigation();

In current navigation you can check the state if it is “loading” then display loading state. For simplicity here a simple message is displayed.

For that instead of <Outlet />, use this

{navigation.state === "loading" ? <h2>Loading!</h2>:<Outlet />}

Then loading message is displayed while data is fetched.

Data Loader in React Router

Using hydratefallbackelement property

You should also add the hydrateFallbackElement to your router configuration, otherwise you may get warning for the same.

{path: "post", element: <PostList />, loader: PostLoader, hydrateFallbackElement: <h2>Loading...</h2>,},

With fetched data

That's all for this topic Data Loader in React Router. If you have any doubt or any suggestions to make please drop a comment. Thanks!


Related Topics

  1. Search Filter Using useSearchParams in React Router
  2. Lazy Loading Routes in React

You may also like-

  1. React Declarative Approach
  2. React Virtual DOM
  3. Controlled and Uncontrolled Components in React
  4. JavaScript Array map() Method With Examples
  5. ArrayList in Java With Examples
  6. Count Number of Times Each Character Appears in a String Java Program
  7. Java Stream API Interview Questions And Answers
  8. What is Client Side Routing in Angular

Monday, June 9, 2025

Lazy Loading Routes in React

In this post we'll see how to lazy load routes in React.

Lazy load brings better performance as components are loaded on demand. During initial render only the required components are loaded so the initial bundle size is small.

When components are loaded lazily as separate chunks that results in splitting of code in separate bundles.

How to implement lazy loading for routes in React Router

React Router version used here is version 7.

Two things that are needed for implementing lazy loading for routes in React

  • Using React.lazy function
  • Using React.Suspense built-in component

Using React.lazy

Using lazy function, you can defer loading components code until it is rendered for the first time. The parameter in the React.lazy function is a function that returns a Promise. This function (which is passed as a parameter) will not be called until the first time you attempt to render the returned component. Note that, both the returned Promise and the Promise's resolved value will be cached, so React will not call this function more than once.

For lazy load routes, wrap the import statement for the components that are mapped to the route, with in the lazy function. With this the component will be loaded only when the route is visited.

const Home = lazy(() => import('./components/routes/home'));
const About = lazy(() => import('./components/routes/about'));

Using React.Suspense

While the lazily loaded component is getting loaded you may want to show some fallback UI in place of the actual UI that can be done using the Suspense component.

With Suspense you can use following props-

  1. children- Actual component that is wrapped with in Suspense.
  2. fallback- UI which is rendered while the actual UI is getting loaded. Suspense will automatically switch to fallback when children suspends, and back to children when the data is ready.

With React Router version 7 it is better to use Suspense with each route definition.

<Route index element={<Suspense fallback={<>Loading....</>}><Home /></Suspense>} />
<Route path="about" element={<Suspense fallback={<div>Loading....</div>}><About /></Suspense>

Lazy load routes React example

Components

As stated above there are Home and About components.

src\components\routes\Home.js

const Home = () => {
    return (
    <>
        <h2>This is home page</h2>
    </>
    )
}

export default Home;

src\components\routes\About.js

const About = () => {
    return (
        <>
            <h2>This is about page</h2>
        </>
    );
}

export default About;

There is also a navigation menu component.

src\components\routes\NavigationNavLink.js

import { NavLink, Outlet } from "react-router";
import "./navigation.css";
const NavigationNavLink = () => {
    const style = (({ isActive, isPending }) => 
        isPending ? "pending" : isActive ? "active" : ""
    );
    return(
        <>
            <nav id="menu" className="navbar navbar-expand-lg bg-dark navbar-dark">
                <div className="container-fluid">
                    <button className="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarMenu" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
                        <span className="navbar-toggler-icon"></span>
                    </button>

                    <div className="collapse navbar-collapse" id="navbarMenu">
                        <ul className="navbar-nav">
                            <li>
                                <NavLink className={style} to="/">
                                    Home
                                </NavLink>
                            </li>
                            <li>
                                <NavLink className={style} to="/about">
                                    About
                                </NavLink>
                            </li>
                        </ul>
                    </div>
                </div>
            </nav>
            <div className="container row mt-2">
                <Outlet />
            </div>
        </>
    );
}

export default NavigationNavLink;

CSS

Uses CSS for active menu hightlight.

src\components\routes\navigation.css

#menu a:link,
#menu a:visited {
    color: gray;
}
#menu a:hover {
    color: white;
}
#menu a.active {
    color: #7FFFD4;
}

#menu a {
    text-decoration: none;
}

#menu ul {
    gap: 1rem;
}

Route Configuration

import { BrowserRouter, Route, RouterProvider, Routes } from 'react-router';
import NavigationNavLink from './components/routes/NavigationNavLink';
import { Suspense, lazy } from 'react';
const Home = lazy(() => simulateDelay(import('./components/routes/Home')));
const About = lazy(() => import('./components/routes/About'));

function App() {
  return (
    <>
      <BrowserRouter >
        
          <Routes>
            <Route path="/" element={<NavigationNavLink />}>
            
                <Route index element={<Suspense fallback={<>Loading....</>}><Home /></Suspense>} />
                <Route path="about" element={<Suspense fallback={<div>Loading....</div>}><About /></Suspense>} />            
             </Route>
          </Routes>
      </BrowserRouter>
    </>
  );
}

function simulateDelay(promise) {
  return new Promise(resolve => {
    setTimeout(resolve, 1000);
  }).then(() => promise);
}

export default App;

Since Home component doesn't have much data so a function is used to simulate delay of 1 second to see the fallback.

Lazy Loading Routes in React

You can also see that components are loaded as separate chunks.

Implement lazy loading routes

That's all for this topic Lazy Loading Routes in React. If you have any doubt or any suggestions to make please drop a comment. Thanks!


Related Topics

  1. Routing in React With Example
  2. Setting 404 Error Page With React Router
  3. useNavigate in React Router to Navigate Programmatically
  4. useSearchParams in React Router - Handling Query Parameters
  5. Search Filter Using useSearchParams in React Router

You may also like-

  1. React Declarative Approach
  2. React Virtual DOM
  3. Controlled and Uncontrolled Components in React
  4. JavaScript Array map() Method With Examples
  5. ArrayList in Java With Examples
  6. Count Number of Times Each Character Appears in a String Java Program
  7. Java Stream API Interview Questions And Answers
  8. What is Client Side Routing in Angular

Sunday, June 8, 2025

Search Filter Using useSearchParams in React Router

In this post we'll see how to implement search filtering (Filtering the items based on the search string with in the same page) using useSearchParams() hook in React Router.

To get more info about useSearchParams() hook, please refer this post- useSearchParams in React Router - Handling Query Parameters

Filter implementation using useSearchParams React example

In the example there is a page that shows product and there is a search box to filter on product name.

import { useEffect, useState } from "react";
import { useSearchParams } from "react-router";

const DUMMY_PRODUCTS = [
    {id:1, name:'Laptop', price:455.50},
    {id:2, name:'Mouse', price:15.89},
    {id:3, name:'USB', price:10.00},
    {id:4, name:'HDD', price:55.50},
];

const ProductFilter = () => {
    const [pName, setpName] = useState('');
    const [filteredProducts, setFilteredProducts] = useState(DUMMY_PRODUCTS);
    const [searchParams, setSearchParams] = useSearchParams();

    const ProductChangeHandler = (event) => {
        event.preventDefault();
        const prodName = event.target.value;
        setpName(prodName);
        // start filtering when atleast 3 characters are entered
        if(prodName.length >= 3){
            setSearchParams({productName:prodName})
        }else{
            setSearchParams();
        }       
    }
    //get the query parameter value
    const searchedProduct = searchParams.get('productName');
    useEffect(()=>{
        if(searchedProduct){
            setFilteredProducts(DUMMY_PRODUCTS
                    .filter(product => product.name.toLowerCase()
                    .includes(searchedProduct.toLowerCase())));
        }else{
            setFilteredProducts(DUMMY_PRODUCTS);
        }

    }, [searchedProduct]);
    
    return (
        <>  
            <h2>Product</h2>
            <div>
            <label>Filter by name</label>
              <input
                type="text"
                value={pName}
                placeholder="Enter product name"
                onChange={ProductChangeHandler}/>
            </div>
            <div className= "row">
                <div className="col-sm-6">
                    <ul className="list-group">
                        {filteredProducts.map(product =>
                            <li className="list-group-item" key={product.id}>{product.name} {product.price}</li>
                        )}
                    </ul>

                </div>
            </div>
        </>

    )
}

export default ProductFilter;
Points to note here-
  1. For search input element there is a onChange event to get the current search value which is maintained in the pName state.
    const [pName, setpName] = useState('');
    

    Searched product is added to the URL as search parameter only after 3 characters are entered, which means filtering starts when at least 3 characters are entered.

    if(prodName.length >= 3){
      setSearchParams({productName:prodName})
    }else{
      setSearchParams();
    }  
    
  2. Parameters from the current URL are extracted using useSearchParams.
  3. Another state is maintained for the filtered products which is the list of products remained after search filtering. Notice that the includes method of the String is used to look for the searched string anywhere in the product name. You can change that implementation for exact match, startsWith or any other implementation based on the requirement.
  4. useEffect has a dependency on the search parameter, if search parameter has a value then the filtering is done based on that value otherwise the original list itself is set to the filtered product state.
  5. Uses bootstrap 5 for styling.

Filtering products

Less than 3 characters

That's all for this topic Search Filter Using useSearchParams in React Router. If you have any doubt or any suggestions to make please drop a comment. Thanks!


Related Topics

  1. Dynamic Route in React Router With Example
  2. Setting 404 Error Page With React Router
  3. Index Route in React Router

You may also like-

  1. React Declarative Approach
  2. React Virtual DOM
  3. React create-react-app Project Structure
  4. JavaScript Rest Parameter
  5. ArrayList in Java With Examples
  6. Count Number of Times Each Character Appears in a String Java Program
  7. Java Stream API Interview Questions And Answers
  8. What is Client Side Routing in Angular

useSearchParams in React Router - Handling Query Parameters

In this post we'll see how to handle URL search parameters or query parameters using useSearchParams() hook in React Router.

What are query parameters

Query parameters are used to pass data in a URL. These parameters are added to the URL after a question mark in the form of key-value pair. You can pass multiple parameters in a URL in that case parameters are separated by an ampersand ('&').

http://example.com/search?name=react&category=web

In the above URL name and category are query parameters with values as react and web respectively.

useSearchParams hook to handle query parameters

Using useSearchParams in React Router you can access and modify query parameters. useSearchParams hook returns an array with two elements; search parameters of the current URL and a function to update them. Using array destructuring you can assign these 2 elements to two variables.

const [searchParams, setSearchParams] = useSearchParams();

You can initialize the search params with a default value, if needed.

const [searchParams, setSearchParams] = useSearchParams("?name=react");

You can access the value of any search parameter by passing the key to get method.

const searchedLanguage = searchParams.get('name');
const searchedCategory = searchParams.get('category');

By using setSearchParams which is a function ( second element in the array retruned by useSearchParams()), you can update the query parameters

setSearchParams({ name: "angular" });
You can also update multiple values or even append a new one.
setSearchParams({ name: "angular", category:"web" });

useSearchParams to access query parameter React example

In the example we'll have a ProductSearch component where user can enter the name of the product. Entered product name is added to the URL as a search param and Product component is called to find the searched product in an array of products.

Route configuration

Routes for the above mentioned components can be configured as-

<Route path="product" element={<RouteProduct />} />  
<Route path="productSearch" element={<ProductSearch />} />

Components

src\components\routes\ProductSearch.js

import { useNavigate } from "react-router";
import { useState } from "react";
const ProductSearch = () => {
    const [pName, setpName] = useState('');
    const navigate = useNavigate();

    const ProductChangeHandler = (event) => {
        event.preventDefault();
        setpName(event.target.value);
    }

    const searchHandler = () => {
        navigate(`/product?productName=${pName}`);
    }
    return (
        <>  
            <h2>Product Search</h2>
            <div>
              <input
                type="text"
                value={pName}
                placeholder="Enter product name"
                onChange={ProductChangeHandler}/>
              <button type="button" onClick={searchHandler}>Search</button>
            </div>
        </>
    )
}

export default ProductSearch;

Points to note here-

  1. There is an input element where user can enter the searched product.
  2. State of the searched product is saved in pName variable.
  3. Using useNavigate() user is programmatically navigated to the path `/product?productName=${pName}` where product name is added as a search parameter in the URL.

src\components\routes\RouteProduct.js

import { useSearchParams } from "react-router";
export const DUMMY_PRODUCTS = [
    {id:1, name:'Laptop', price:455.50},
    {id:2, name:'Mouse', price:15.89},
    {id:3, name:'USB', price:10.00},
    {id:4, name:'HDD', price:55.50}
];

const RouteProduct = () => {
    const [searchParams, setSearchParams] = useSearchParams();
    const searchText = searchParams.get('productName');
    //console.log('' , searchText)
    const products = DUMMY_PRODUCTS.filter(product => product.name.toLowerCase() === searchText.toLowerCase());
    return(
        <>
        <div className= "row">
            <div className="col-sm-6">
                <ul className="list-group">
                    {products.map(product =>
                        <li className="list-group-item" key={product.id}>{product.name} {product.price}</li>
                    )}
                </ul>
            </div>
        </div>
        </>
    )
}

export default RouteProduct;

Points to note here-

  1. A hardcoded array of products is used here against which search will be done.
  2. useSearchParams() hook is used here to extract the search parameter value from the URL.
    const searchText = searchParams.get('productName');
    
  3. Bootstrap 5 is used for styling here.

Product search page

useSearchParams in React Router

After clicking search button

Handling Query Parameters React Router

This type of filtering functionality gives a better user experience if done in the same page. Refer post- Search Filter Using useSearchParams in React Router to see how to implement search filtering.

That's all for this topic useSearchParams in React Router - Handling Query Parameters. If you have any doubt or any suggestions to make please drop a comment. Thanks!


Related Topics

  1. Dynamic Route in React Router With Example
  2. Setting 404 Error Page With React Router
  3. Index Route in React Router

You may also like-

  1. React Declarative Approach
  2. React Virtual DOM
  3. React create-react-app Project Structure
  4. JavaScript Rest Parameter
  5. ArrayList in Java With Examples
  6. Count Number of Times Each Character Appears in a String Java Program
  7. Java Stream API Interview Questions And Answers
  8. What is Client Side Routing in Angular

useNavigate in React Router to Navigate Programmatically

In this post we'll see how to use useNavigate() hook in React Router to navigate programmatically between routes in your application.

useNavigate() hook in React

useNavigate() hook returns a function. You can pass a path to that function to navigate to that path. For example, if you want to navigate from current path to "/dashboard/setting"

const navigate = useNavigate();
navigate("/dashboard/setting");

You can also pass options as second argument to the function. This second argument is an optional object that can include additional configuration settings. Full signature is-

navigate(
  to: To,
  options?: {
    flushSync?: boolean;
    preventScrollReset?: boolean;
    relative?: RelativeRoutingType;
    replace?: boolean;
    state?: any;
    viewTransition?: boolean;
  }
)

Explanation of some of the options is as given below-

  1. preventScrollReset- You can prevent the scroll position from being reset by using { preventScrollReset: true } option.
  2. relative- When passing path to the navigate function you can pass an absolute path like navigate(“/about”) or a relative path like navigate('../settings'). The relative option can be set to "route" or "path". By default, relative option is set to “route” which uses route hierarchy so ".." will remove all path segments of the current route. Which means if you are currently at /user/account and use navigate('../settings') then it will navigate to /settings.
    Using relative option as "path" will let you navigate relative to path. Which means if you are currently at /user/account and use navigate('../settings', { relative: "path" }) then it will go one level up in the current path segment and navigate to /user/settings.
  3. replace- By default replace is false which means navigate adds a new entry to the history stack. Using {replace:true} option will remove the current entry in the history stack, replacing it with a new one. A use case for this is if you are navigating to login but doesn't want user to redirect to login page by pressing back button.
    	navigate('/login', { replace: true });
    
  4. state- If you use state to set any value, that will be available on the location object on the next page. In the target page you can access the state using useLocation() hook.
    navigate('/user', { state: { account: 'disable' } });
    
    using useLocation().state.account, in the target page will return value as "disable".

Navigating back and forward in history stack

By using navigate(number) where number can be a positive or negative number you can navigate forward/back (equivalent to pressing forward or back button in the browser). For example-

// back
navigate(-1);

// forward
navigate(1);

useNavigate React example

In the example we'll have UserRegistrationForm component and UserSuccess component. When user clicks the submit button after filling the form properly, user should be navigated to /userSuccess path.

Routing configuration

import { BrowserRouter, Route, Routes } from 'react-router';
import About from './components/routes/About';
import Home from './components/routes/Home';
import NavigationNavLink from './components/routes/NavigationNavLink';
import UserRegistrationForm from './components/routes/UserRegistrationForm';
import UserSuccess from './components/routes/UserSuccess';


function App() {
  return (
    <>
      <BrowserRouter >

        <Routes>
          <Route path="/" element={<NavigationNavLink />}>
            <Route path="home" element={<Home />} />
            <Route path="about" element={<About />} />
            <Route path="user" element={<UserRegistrationForm />} />
            <Route path="userSuccess" element={<UserSuccess />} />
          </Route>
        </Routes>
      </BrowserRouter>
    </>

  );
}

export default App;

Navigation menu

src\components\routes\NavigationNavLink.js

import { NavLink, Outlet } from "react-router";
import "./navigation.css";
const NavigationNavLink = () => {
    const style = (({ isActive, isPending }) => 
        isPending ? "pending" : isActive ? "active" : ""
    );
    return(
        <>
            <nav id="menu" className="navbar navbar-expand-lg bg-dark navbar-dark">
                <div className="container-fluid">
                    <button className="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarMenu" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
                        <span className="navbar-toggler-icon"></span>
                    </button>

                    <div className="collapse navbar-collapse" id="navbarMenu">
                        <ul className="navbar-nav">
                            <li>
                                <NavLink className={style} to="/home">
                                    Home
                                </NavLink>
                            </li>
                            <li>
                                <NavLink className={style} to="/user">
                                    UserRegistration
                                </NavLink>
                            </li>
                            <li>
                                <NavLink className={style} to="/about" >
                                    About
                                </NavLink>
                            </li>
                        </ul>
                    </div>
                </div>
            </nav>
            <div className="container row mt-2">
                <Outlet />
            </div>
        </>
    );
}

export default NavigationNavLink;

CSS styling for the links

src\components\routes\navigation.css

#menu a:link,
#menu a:visited {
    color: gray;
}
#menu a:hover {
    color: white;
}
#menu a.active {
    color: #7FFFD4;
}

#menu a {
    text-decoration: none;
}

#menu ul {
    gap: 1rem;
}

Components

src\components\routes\Home.js

const Home = () => {
    return (
    <>
        <h2>This is home page</h2>
    </>
    )
}

export default Home;

src\components\routes\About.js

const About = () => {
    return (
        <>
            <h2>This is about page</h2>
        </>
    );
}

export default About;

src\components\routes\UserRegistrationForm.js

A simple user registration form with 2 fields name and age.

import { useState } from "react";
import { useNavigate } from "react-router";

const UserRegistrationForm = () => {
    const [name, setName] = useState('');
    const [age, setAge] = useState('');
    const [error, setError] = useState('');
    const navigate = useNavigate();

    const nameChangeHandler = (event) => {
        const name = event.target.value;
        const re = /^[A-Za-z]+$/;
        if (name === "" || re.test(name)) {
            setName(name);
        }
        //setName();
    }
    const ageChangeHandler = (event) => {
        const age = event.target.value;
        setAge(age);
    }

    const formSubmitHandler = (event) => {
        event.preventDefault();
        if(name === "" || age === "" ){
            console.log('error');
            setError("Either name or age is not filled");
            return false;
        }
        
        //If both fields are filled navigate to UserSuccess
        navigate("/userSuccess");

    }
    
    return (
        <>
            {error && <p style={{color:"red"}}>{error}</p>}
            <form onSubmit={formSubmitHandler}>
                <label htmlFor="name">Name: </label>
                <input type="text" id="name" value={name} onChange={nameChangeHandler}></input>
                <label htmlFor="age">Age: </label>
                <input type="number" id="age" value={age} onChange={ageChangeHandler}></input>
                <br /><br />
                <button type="submit" >Submit</button>
            </form>
        </>
    );
}

export default UserRegistrationForm;

Some points to note here-

  1. useNavigate() hook is used here to navigate programmatically to another route.
  2. In the formSubmitHandler function there is a validation to check if both name and age fields have values or not. If yes then navigate to "/userSuccess" path. If not then set the error state and remain in the same registration form.

src\components\routes\UserSuccess.js

const UserSuccess = () => {

    return(
        <h3>User registered successfully!</h3>
    )
}

export default UserSuccess;

In UserRegistrationForm

useNavigate in React Router

On clicking submit after filling the form

If there is error in the form

That's all for this topic useNavigate in React Router to Navigate Programmatically. If you have any doubt or any suggestions to make please drop a comment. Thanks!


Related Topics

  1. Dynamic Route in React Router With Example
  2. Setting 404 Error Page With React Router
  3. Index Route in React Router

You may also like-

  1. React Declarative Approach
  2. React Virtual DOM
  3. React create-react-app Project Structure
  4. JavaScript Rest Parameter
  5. ArrayList in Java With Examples
  6. Count Number of Times Each Character Appears in a String Java Program
  7. Java Stream API Interview Questions And Answers
  8. What is Client Side Routing in Angular

Thursday, June 5, 2025

Setting 404 Error Page With React Router

In this post we'll see how to set up an error page (Page not found page) in React Router.

Why do we need error page

If user tries to access URL that doesn't match any configured routes in your application an empty page is displayed to the user. To avoid that you can set a custom error page that will be displayed in case of such routing error. That provides for a better user experience rather than just showing an empty page.

How to set up error page

React Router provides different ways to implement an error or page not found page.

  • To use a wild card route (with path as "/*") as a catch all route.
  • Using useRouteError() hook with errorElement prop.

1. Setting up 404 page using wild card route

If you are using a catch all route to set up an error page then the steps are as given below.

  1. Create an error component that would be displayed in case there is no match for the URL.
  2. In your route configuration provide route definition for error page (path as “/*”) as the last one, otherwise it will match all the routes and you will always end up in the error component.

Error Component

Component that will be rendered if there is any error.

src\components\routes\ErrorPage.js

const ErrorPage = () => {
    return (
        <div>
          <h2 style={{color: "red"}}>Page Not Found (404) error!!</h2>
          <p>Page you are trying to access doesn't exist.</p>
        </div>
    );
}

export default ErrorPage;

Route Configuration

import { BrowserRouter, Route, Routes } from 'react-router';
import About from './components/routes/about';
import Home from './components/routes/home';
import NavigationNavLink from './components/routes/NavigationNavLink';
import ErrorPage from './components/routes/ErrorPage';

function App() {
  return (
    <>
      <BrowserRouter >

        <Routes>
          <Route path="/" element={<NavigationNavLink />}>
            <Route index element={<Home />} />
            <Route path="about" element={<About />} />
            <Route path="*" element={<ErrorPage />} />
          </Route>
        </Routes>
      </BrowserRouter>
    </>
  );
}

export default App;

To get code for other components please refer this post- Using NavLink in React Router

With that if you try to access any path that doesn't exist.

2. Using useRouteError() hook with errorElement prop

Another way to set up error page is to use useRouterError() hook but that works with data and framework modes not with declarative mode.

Error Component

Component that will be rendered if there is any error.

src\components\routes\ErrorPage.js

import { isRouteErrorResponse, useRouteError } from "react-router";

const ErrorPage = () => {
  const error = useRouteError();
  console.log(error);
  if (isRouteErrorResponse(error)) {
    return (
      <>
        <h1>
          {error.status} {error.statusText}
        </h1>
        <p>{error.data}</p>
      </>
    );
  } else if (error instanceof Error) {
    return (
      <div>
        <h1>Error</h1>
        <p>{error.message}</p>
        <p>The stack trace is:</p>
        <pre>{error.stack}</pre>
      </div>
    );
  } else {
    return <h1>Unknown Error</h1>;
  }
}

export default ErrorPage;

As you can see useRouteError() hook is used to get access to the error thrown during an ActionFunction, LoaderFunction, or component render.

Using isRouteErrorResponse you can check if the given error is an ErrorResponse generated from a 4xx/5xx Response thrown from an action/loader.

Since this will work with data or framework mode so route configuration is done using createBrowserRouter function in a separate file.

src\components\routes\Route.js
import { createBrowserRouter } from "react-router";
import About from "./About";
import Home from "./Home";
import NavigationNavLink from "./NavigationNavLink";
import ErrorPage from "./ErrorPage";


export const route = createBrowserRouter([
      {path: "/", element: <NavigationNavLink />, errorElement: <ErrorPage />,
       children: [
         {index: true, element: <Home /> },
         {path: "/about", element: <About />}
       ]
      }
])

You can see the Error page is set using the errorElement prop in the parent route. Even if an error occurs in the Child component, it will bubble up to the Root route where <ErrorPage /> component defined in the errorElement prop will catch that error.

In App.js add the Route Provider.

import { route } from './components/routes/Route';
import { RouterProvider } from 'react-router';

function App() {
  return (
    <RouterProvider router={route}></RouterProvider>
  );
}

export default App;

To get code for other components please refer this post- Using NavLink in React Router

With that if you try to access any path that doesn't exist.

That's all for this topic Setting 404 Error Page With React Router. If you have any doubt or any suggestions to make please drop a comment. Thanks!


Related Topics

  1. Index Route in React Router
  2. Nested Route in React Router With Example
  3. Dynamic Route in React Router With Example

You may also like-

  1. React Declarative Approach
  2. React Virtual DOM
  3. React create-react-app Project Structure
  4. JavaScript Rest Parameter
  5. ArrayList in Java With Examples
  6. Count Number of Times Each Character Appears in a String Java Program
  7. Java Stream API Interview Questions And Answers
  8. What is Client Side Routing in Angular

Dynamic Route in React Router With Example

In this post we'll see what are dynamic routes in React.

When you configure a route like this-

<Route path="about" element={<About />} />

That is considered a static route which matches the /about path segment as it is.

Now, consider a scenario where you want to create a dynamic path where part of the URL is dynamic, which may change. For example, if you have a product page that shows product names and clicking on any product displays the details of that product. This means you need to pass id of the clicked product so that you can get details for that particular product.

In this case you can add the id part as dynamic path segment so that any path in this format /product/{ID} is mapped to the same route.

How to create dynamic route in React

In a route, if a path segment starts with : then it becomes a "dynamic segment".

<Route path="product/:productId" element={<ProductDetail />} />

Here :productid is the dynamic part and it will match any URL like- product/2, product/15 and so on.

You can have multiple dynamic segments in one route path:

<Route
  path="/category/:categoryId/product/:productId"
  element={<ProductDetail />}
/>

How to retrieve route parameters

When the route matches the URL, the dynamic segment with in the URL will be parsed and provided as params to other router APIs like useParams.

useParams() hook in react-router can be used to get values of dynamic segments in route path. This hook returns an object of key/value pairs of the dynamic params from the current URL that were matched by the routes.

For example, if /product/15 matches the route path /product/:productId then useParams() hook will return an object as { productId:15}

const params = useParams();

will assign this object to params.

Then you can get the specific route parameter like this-

params.productId

Or you can use object destructuring to get the productid param from the object returned by useParams() hook.

let { productid } = useParams();

React example - Dynamic routing

In this example there will be a Product page displaying the available product names and a ProductDetail page that shows details for the selected product. ProductId is passed as dynamic path segment.

Route Configuration

App.js

import { BrowserRouter, Route, Routes } from 'react-router';
import About from './components/routes/About';
import Home from './components/routes/Home';
import NavigationNavLink from './components/routes/NavigationNavLink';
import RouteProduct from './components/routes/RouteProduct';
import RouteProductDetails from './components/routes/RouteProductDetails';

function App() {
  return (
    <>
      <BrowserRouter >

        <Routes>
          <Route path="/" element={<NavigationNavLink />}>
            <Route index element={<Home />} />
            <Route path="product" element={<RouteProduct />}> 
              <Route path=":productId" element={<RouteProductDetails />} /> 
            </Route>
            <Route path="about" element={<About />} />
          </Route>
        </Routes>
      </BrowserRouter>
    </>
  );
}

export default App;

Navigation Menu

src\components\routes\NavigationNavLink.js

import { NavLink, Outlet } from "react-router";
import "./navigation.css";
const NavigationNavLink = () => {
    const style = (({ isActive, isPending }) => 
        isPending ? "pending" : isActive ? "active" : ""
    );
    return(
        <>
            <nav id="menu" className="navbar navbar-expand-lg bg-dark navbar-dark">
                <div className="container-fluid">
                    <button className="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarMenu" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
                        <span className="navbar-toggler-icon"></span>
                    </button>

                    <div className="collapse navbar-collapse" id="navbarMenu">
                        <ul className="navbar-nav">
                            <li>
                                <NavLink className={style} to="/">
                                    Home
                                </NavLink>
                            </li>
                            <li>
                                <NavLink className={style} to="/product">
                                    Product
                                </NavLink>
                            </li>
                            <li>
                                <NavLink className={style} to="/about">
                                    About
                                </NavLink>
                            </li>
                        </ul>
                    </div>
                </div>
            </nav>
            <Outlet />
        </>
    );
}

export default NavigationNavLink;

CSS styling

src\components\routes\navigation.css

#menu a:link,
#menu a:visited {
    color: gray;
}
#menu a:hover {
    color: white;
}
#menu a.active {
    color: #7FFFD4;
}

#menu a {
    text-decoration: none;
}

#menu ul {
    gap: 1rem;
}

Components

src\components\routes\Home.js

const Home = () => {
    return (
    <>
        <h2>This is home page</h2>
    </>
    )

}

export default Home;

src\components\routes\About.js

const About = () => {
    return <h2>This is about page</h2>
}

export default About;

src\components\routes\RouteProduct.js

Uses Bootstrap 5 styling.

import { Link, Outlet } from "react-router";

export const DUMMY_PRODUCTS = [
    {id:1, name:'Laptop', price:455.50},
    {id:2, name:'Mouse', price:15.89},
    {id:3, name:'USB', price:10.00},
    {id:4, name:'HDD', price:55.50}
];

const RouteProduct = () => {

    return(
        <>
        <div className= "row">
            <div className="col-sm-4">
                <h2>Products</h2>
 
                <ul className="list-group">

                    {DUMMY_PRODUCTS.map(product =><li className="list-group-item" key={product.id}> 
                        <Link to={`/product/${product.id}`}>{product.name}</Link></li>
                    )}
                </ul>

            </div>
            <div className="col-sm-4">
                <Outlet />
            </div>
        </div>
        </>
    )
}

export default RouteProduct;

Some of the points to note here-

  1. An array of product objects (hardcoded) is initialized with 3 properties- id, name, price.
  2. While iterating through the array, with each name a link is created to pass /product/{id} as path.

src\components\routes\RouteProductDetails.js

import { useParams } from "react-router"
import { DUMMY_PRODUCTS } from "./RouteProduct";

const RouteProductDetails = () => {
    const params = useParams();
    const product = DUMMY_PRODUCTS.find(product => product.id === +params.productId);
    return (
        <>
            <h2>Product Details</h2>
            <div className="row">
                <div className="col-xs-6">
                    <span>Name: </span>{product.name}
                </div>
            </div>
            <div className="row">
                <div className="col-xs-6">
                    <span>Price: </span>{product.price}
                </div>

            </div>
        </>
    )
}

export default RouteProductDetails;

Some of the important points here-

  1. Using useParams() hook, values of the dynamic path segment is retrieved.
  2. Find method of the Array is used to find the product that matches the passed Id.

When menu option Product is clicked.

Dynamic Route in React

Clicking on one of the products-

Dynamic Route in React router

That's all for this topic Dynamic Route in React Router With Example. If you have any doubt or any suggestions to make please drop a comment. Thanks!


Related Topics

  1. Routing in React With Example
  2. Link in React Router With Example

You may also like-

  1. React Declarative Approach
  2. React Virtual DOM
  3. React create-react-app Project Structure
  4. JavaScript Rest Parameter
  5. ArrayList in Java With Examples
  6. Count Number of Times Each Character Appears in a String Java Program
  7. Java Stream API Interview Questions And Answers
  8. What is Client Side Routing in Angular

Wednesday, June 4, 2025

Nested Route in React Router With Example

In this post we'll see how to configure nested routes or child routes in React Router.

Nested routes in React

You can nest a route inside another route which establishes a parent route-child route relationship.

<Route path="parent" element ={<Parent />}>
    <Route path="child" element = {<Child/>}/>
</Route >

With this kind of nested route, child URL path is relative to the path to parent which means this route configuration creates both /parent and /parent/child URLs.

With nested routes you can also control the rendering of the child routes which can be rendered with in the context of parent using the Outlet component.

import { Outlet } from "react-router";
export default function Parent() {
  return (
    <div>
      <h1>Parent</h1>
      {/* will render <Child/> */}
      <Outlet />
    </div>
  );
}

Example of nested route can be seen in this post- Using NavLink in React Router where route configuration is as shown here-

<Route path="/" element {<NavigationNavLink />}>
  <Route index element={<Home />} />
  <Route path="about" element={<About />} />
</Route>

With this configuration "/" and "/about" are nested paths with in the root URL.

Dynamic nested route in React

You can also nest a dynamic route so that dynamic path segment can be mapped to a route. For example, if you have a product page that shows product names and clicking on any product displays the details of that product. This means you need to add the id part as dynamic path segment so that any path in this format /product/{ID} is mapped to the route.

<Routes>
  <Route path="/" element={<NavigationNavLink />}>
    <Route index element={<Home />} />
    <Route path="product" element={<RouteProduct />}> 
      <Route path=":productId" element={<RouteProductDetails />} /> 
    </Route>
    <Route path="about" element={<About />} />
  </Route>
</Routes>

As you can see here this particular nested route-

<Route path="product" element={<RouteProduct />}> 
  <Route path=":productId" element={<RouteProductDetails />} /> 
</Route>

Will map to any path like- /product/101, /product/23 and so on.

That's all for this topic Nested Route in React Router With Example. If you have any doubt or any suggestions to make please drop a comment. Thanks!


Related Topics

  1. Index Route in React Router
  2. Routing in React With Example
  3. React Declarative Approach
  4. React Virtual DOM

You may also like-

  1. React create-react-app Project Structure
  2. JavaScript Rest Parameter
  3. ArrayList in Java With Examples
  4. Count Number of Times Each Character Appears in a String Java Program
  5. Java Stream API Interview Questions And Answers
  6. What is Client Side Routing in Angular

Index Route in React Router

In this post we'll see what are Index routes in React Router.

You can think of index routes as nested routes that don't have their own path. Consider this route definition which was used in post- Using NavLink in React Router.

<Route path="/" element={<NavigationNavLink />}>
  <Route path="/" element={<Home />} />
  <Route path="about" element={<About />} />
</Route>

Requirement here is; when the user accesses the root URL, navigation menu should be rendered (NavigationNavLink component) and Home component should also be rendered. That is why path="/" is used in both the routes.

In such scenario child route can be defined as an index route making it a default child route which should be rendered into their parent's <Outlet/> when the path for the parent route is accessed.

If we take the route definition as stated above, child route can be configured with the index prop to make it an index route.

<Route path="/" element={<NavigationNavLink />}>
  <Route index element={<Home />} />
  <Route path="about" element={<About />} />
</Route>

Using index with createBrowserRouter

If you are using createBrowserRouter to configure routes then index routes are defined by setting index: true on a route object without a path

export const route = createBrowserRouter([
      {path: "/", element: <NavigationNavLink />,
      children: [
         {index: true, element: <Home /> },
         {path: "/about", element: <About />}
      ]
      }
])

That's all for this topic Index Route in React Router. If you have any doubt or any suggestions to make please drop a comment. Thanks!


Related Topics

  1. Routing in React With Example
  2. Link in React Router With Example

You may also like-

  1. React Declarative Approach
  2. React Virtual DOM
  3. React create-react-app Project Structure
  4. JavaScript Rest Parameter
  5. ArrayList in Java With Examples
  6. Count Number of Times Each Character Appears in a String Java Program
  7. Java Stream API Interview Questions And Answers
  8. What is Client Side Routing in Angular