Tuesday, June 17, 2025

useSubmit() Hook in React Router

In the post Actions in React Router For Data Mutation we saw how you can create a resource (POST method) using actions and Form component in React Router. Another way (more explicit way) to submit a form is to use useSubmit() hook in React Router. In this post we'll see how to programmatically submit a form using useSubmit() hook.

useSubmit() hook in React Router

useSubmit hook returns a function.
const submit = useSubmit();

This returned function (named submit here) takes two arguments. First is the data that has to be submitted which will be wrapped as form data object. Another argument is an optional object where you can pass method with which you can specify the HTTP verb to use when the form is submitted. Supports "get", "post", "put", "delete", and "patch". Another property you can pass is action with which you can provide the URL to submit the form data to. If no action is specified, this defaults to the closest route in context.

submit(event.currentTarget, {method:"POST", action:"/post/new"});

useSubmit() hook React example

In this example we'll use an action function to create a resource. 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. This example is same as what is already shown in this post Actions in React Router For Data Mutation except the change for programmatically submitting the form having post data using useSubmit() hook.

Component that changes is the PostForm component. Now rather than using the Form component normal <form> element is used. On submitting the form handler function is called that submits the form programmatically.

src\components\routes\PostForm.js

import { useSubmit } from "react-router";

const PostForm = () => {
   
  const submit = useSubmit();
  const formSubmitHandler = (event) => {
    event.preventDefault();
    //console.log(event.currentTarget);
    submit(event.currentTarget, {method:"POST", action:"/post/new"});
  }
  return(
    <div className="container">
      <h2>Post</h2>
      <form onSubmit={formSubmitHandler}> 
        {/* <div className="mb-2 mt-2">
            <label className="form-label" htmlFor="id">ID: </label>
            <input className="form-control" type="text" name="id" id="id" defaultValue={postData?postData.id:""}></input>
        </div> */}
        <div className="mb-2">
          <label className="form-label" htmlFor="title">Title: </label>
          <input className="form-control" type="text" name="title" id="title"></input>
        </div>
        <div className="mb-2">
          <label className="form-label" htmlFor="body">Body: </label>
          <textarea className="form-control" type="text" name="body" id="body"></textarea>
        </div>
        <div className="mb-2">
          <label className="form-label" htmlFor="userId">User ID: </label>
          <input className="form-control" type="text" name="userId" id="userId" rows="3"></input>
        </div>
        <button className="btn btn-info" type="submit">Save</button>
      </form>
    </div>
  )
}

export default PostForm;

In the handler function (formSubmitHandler()) for form submit, function returned by useSubmit hook is used to submit data. In the submit function first argument is event.currentTarget which is the form itself. As the second argument an object is passed with method and action properties. Here method value is POST and action is "/post/new" path where the action function is defined. If we don't specify action path here that will also work because action defaults to the closest route in context which is anyway "/post/new".

src\components\routes\PostAdd.js

This component is called when the route path is "/post/new".

import { redirect } from "react-router";
import PostForm from "./PostForm"

const PostAdd =() => {
  return(
    <PostForm />
  )
}

export default PostAdd;

export async function action({request, params}){
  const data = await request.formData();
  const postData = {
    title:data.get("title"),
    body:data.get("body"),
    userId:data.get("userId")
  }
  const response = await fetch('https://jsonplaceholder.typicode.com/posts', 
    {
      method: 'POST', 
      headers: {                
          'Content-type': 'application/json',                 
      },
      body: JSON.stringify(postData)
    },
      
  );
  if(!response.ok){
    throw new Response('Error while saving post data', {status:500});
  } 
  const responseData = await response.json();
  //console.log(responseData);
  return redirect('/post');
}

Some of the points to note here-

  1. Action function receives an object that has properties like request, params.
  2. Request object has a method formData() that returns form data.
  3. On that data object you can call get() method to get the form field value by passing the form field name. The name you pass in get method should match the name you gave in the input element with in the form. Refer PostForm to check the name attribute in each input element.
  4. With action function or loader function it is recommended to use redirect function rather than useNavigate to navigate to another page. That’s what is done here, if post is added successfully redirect to “/post” path.

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


Related Topics

  1. Data Loader With Dynamic Routes in React
  2. useRouteLoaderData() Hook in React Router
  3. Using NavLink in React Router
  4. Index Route in React Router

You may also like-

  1. JVM Run-Time Data Areas - Java Memory Allocation
  2. Difference Between Abstract Class And Interface in Java
  3. Armstrong Number or Not Java Program
  4. Spring Boot + Data JPA + MySQL REST API CRUD Example

Actions in React Router For Data Mutation

In the post Data Loader in React Router we have seen how to fetch data using loaders in React Router. Same way action in React Router is a function that handles data mutations.

If you have a CRUD operation then Create, Delete and Update part (data mutation operations) can be done by actions. For read operation (data fetching) you can use loader.

How to call actions

You can execute action function by using one of the following ways.

  1. Actions are called declaratively through Form component provided by React Router. That is what we'll see in this article
  2. Imperatively through useSubmit() hook
  3. By using fetcher.Form or fetcher.submit

Using action to Post data- React Example

In this example we'll use an action function to create a resource. 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

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:"new", element: <PostAdd />, action: postAddAction}
     ]
    },
   ]
  },
])

Here we have 2 levels of nested routes. Root parent is Navigation menu component. Another level is for PostLayout component for path "/post" which renders the list of posts and another nested route definition for "/post/new" to create a new post.

Notice the use of action property with route definition for "/post/new".

{path:"new", element: <PostAdd />, action: postAddAction}

That points to the action function called to add post data.

Components

For Navigation Menu, Home and ErrorPage components you can get the code from this post- Data Loader in React Router.

src\components\routes\PostLayout.js

This component acts a parent route component for all the Post related functionality.

import { Outlet } from &quot;react-router&quot;;
const PostLayout = () =&gt; {
    return(
        &lt;div className=&quot;mx-2&quot;&gt;
            &lt;Outlet /&gt;
        &lt;/div&gt;

    )
}

export default PostLayout;

src\components\routes\PostList.js

This component lists all the fetched post data.

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

const PostList = () => {
  const postData = useLoaderData();
  const navigate = useNavigate();
  const PostAddHandler = () => {
    navigate("./new");
  }
  return(
    <> 
      <button className="btn btn-info" onClick={PostAddHandler}>Add New Post</button>
      <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. Data fetched by the loader function is retrieved using the useLoaderData() hook. You can use useLoaderData here as loader data is needed with in the current route.
  4. There is also a button to add new post. Clicking that button results in navigation to “/new” relative to current route path.
Actions in React Router

src\components\routes\PostForm.js

This component renders a form to enter data for new post.

import { Form } from "react-router";

const PostForm = () => {
  return(
    <div className="container">
      <h2>Post</h2>
      <Form method="post"> 
        <div className="mb-2">
          <label className="form-label" htmlFor="title">Title: </label>
          <input className="form-control" type="text" name="title" id="title"></input>
        </div>
        <div className="mb-2">
          <label className="form-label" htmlFor="body">Body: </label>
          <textarea className="form-control" type="text" name="body" id="body"></textarea>
        </div>
        <div className="mb-2">
          <label className="form-label" htmlFor="userId">User ID: </label>
          <input className="form-control" type="text" name="userId" id="userId" rows="3"></input>
        </div>
        <button className="btn btn-info" type="submit">Save</button>
      </Form>
    </div>
  )
}

export default PostForm;

Some of the points to note here-

  1. Form component used here is provided by React Router which is a progressively enhanced HTML <form> that submits data to actions. When the form is submitted form data is provided to the action.
  2. With Form component you can use props like action and submit.
  3. With action prop you can provide the URL to submit the form data to. If no action is specified, this defaults to the closest route in context.
  4. With method prop you can specify the HTTP verb to use when the form is submitted. Supports "get", "post", "put", "delete", and "patch".

src\components\routes\PostAdd.js

This component is called when the route path is "/post/new".

import { redirect } from "react-router";
import PostForm from "./PostForm"

const PostAdd =() => {
  return(
    <PostForm />
  )
}

export default PostAdd;

export async function action({request, params}){
  const data = await request.formData();
  const postData = {
    title:data.get("title"),
    body:data.get("body"),
    userId:data.get("userId")
  }
  const response = await fetch('https://jsonplaceholder.typicode.com/posts', 
    {
      method: 'POST', 
      headers: {                
          'Content-type': 'application/json',                 
      },
      body: JSON.stringify(postData)
    },
      
  );
  if(!response.ok){
    throw new Response('Error while saving post data', {status:500});
  } 
  const responseData = await response.json();
  console.log(responseData);
  return redirect('/post');
}

Some of the points to note here-

  1. Action function receives an object that has properties like request, params.
  2. Request object has a method formData() that returns form data.
  3. On that data object you can call get() method to get the form field value by passing the form field name. The name you pass in get method should match the name you gave in the input element with in the form. Refer PostForm to check the name attribute in each input element.
  4. With action function or loader function it is recommended to use redirect function rather than useNavigate to navigate to another page. That’s what is done here, if post is added successfully redirect to “/post” path.
data mutation in React Router

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


Related Topics

  1. useRouteLoaderData() Hook in React Router
  2. Search Filter Using useSearchParams in React Router
  3. Setting 404 Error Page With React Router
  4. Lazy Loading Routes in React

You may also like-

  1. Controlled and Uncontrolled Components in React
  2. JavaScript Array slice() method With Examples
  3. Java Sealed Classes and Interfaces
  4. Java split() Method - Splitting a String
  5. Accessing Characters in Python String
  6. Angular Route Resolver - Passing Data Dynamically

useRouteLoaderData() Hook in React Router

In this post we'll see how to use useRouteLoaderData() hook in React Router to access data loaded by loader function. useRouteLoaderData() hook makes data available any where within the component hierarchy, that's how it differs from another similar hook useLoaderData() which can access data from the closest route loader.

The benefit of useRouteLoaderData() hook is that it enables sharing of data across components. Component can access data from parent route or sibling routes.

How to use useRouteLoaderData in React Router

You need to first import it.

import { useRouteLoaderData } from "react-router";

You need to pass route ID of the given route to useRouteLoaderData() and it returns loader data for that route. For example if you have defined route as given below with id property.

{path: ":postId", id: "post-detail", loader: postDetailsLoader}

Then you can use it like this with useRouteLoaderData to get data fetched by postDetailsLoader loader function.

const postData = useRouteLoaderData("post-detail");

Route IDs are created automatically too. They are simply the path of the route file relative to the app folder, so you can use, automatically created id also rather than creating one manually.

useRouteLoaderData() hook React example

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.

Let's try to understand how useRouteLoaderData() hook works with an example. Suppose we have the following route configuration.

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", id: "post-detail", loader:PostDetailsLoader,
           children: [
              {index:true, element: <PostDetails />, hydrateFallbackElement: <h2>Loading...</h2>},
              {path:"edit", element: <PostEdit />}
           ]
        },
        {path:"new", element: <PostAdd />, action:PostAddAction}
     ]
    },
   ]
  },
])

Here we have 3 levels of nesting. Root parent is Navigation menu component. Another level is for PostLayout component for path "/post" which renders the list of posts.

Yet another level is dynamic route for "/post/POST_ID" which renders details for the selected post through PostDetails component. There is another child with in the same hierarchy to render a form to edit the selected post, for that path is "post/POST_ID/edit".

When you are showing edit form for editing a post, you want that form pre-filled with the selected post data (fetch post by id). Same way when you are going to post details page that shows data for the selected post. Since both of these components need data by postId, so the loader function can be shared. If you notice the route configuration, that's what has been done, loader is configured at the parent level.

{path: ":postId", id: "post-detail", loader:PostDetailsLoader,
  children: [
    {index:true, element: <PostDetails />, hydrateFallbackElement: <h2>Loading...</h2>},
    {path:"edit", element: <PostEdit />}
  ]
},

With the above route configuration, trying to get loader data using useLoaderData() hook won't work as that can get data only from the closest route loader where as in this case we need data from the parent route. In this kind of scenario useRouteLoaderData() hook can be used as that can get data from the parent route.

Components

For Navigation Menu, Home and ErrorPage components you can get the code from this post- Data Loader in React Router.

src\components\routes\PostLayout.js

This component acts a parent route component for all the Post related functionality.

import { Outlet } from &quot;react-router&quot;;
const PostLayout = () =&gt; {
    return(
        &lt;div className=&quot;mx-2&quot;&gt;
            &lt;Outlet /&gt;
        &lt;/div&gt;

    )
}

export default PostLayout;

src\components\routes\PostList.js

This component lists all the fetched post data.

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. You can use useLoaderData here as loader data is needed with in the current route.
  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
useRouteLoaderData() Hook in React Router

src\components\routes\PostDetails.js

This component renders details of the post whose ID is selected. Also shows buttons to edit or delete post.

import { useNavigate, useRouteLoaderData } from "react-router";

const PostDetails = () => {
  const postData = useRouteLoaderData("post-detail");
  const navigate = useNavigate();
  const editHandler = () => {
      navigate("./edit");
  }
  return(
    <>
      <h2 className="text-center">{postData.title}</h2>
      <div>{postData.body}</div>
      <div className="text-center">
        <button className="btn btn-success " onClick={editHandler}>Edit</button> 
        <button className="btn btn-danger mx-2">Delete</button>
      </div>
    </>
  )
}

export default PostDetails;

export async function loader({request, params}){
  const url = "https://jsonplaceholder.typicode.com/posts/"+params.postId
  const response = await fetch(url);
  console.log(response);
  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. Data fetched by the loader function is retrieved using the useRouteLoaderData() hook. That is needed as loader though defined here is actually called at the parent route level
    {path: ":postId", id: "post-detail", loader: postDetailsLoader,
      children: [
        {index:true, element: <PostDetails />, hydrateFallbackElement:  <h2>Loading...</h2>},
    
  3. Clicking on Edit button navigates to /post/POST_ID/edit path
  4. Delete button functionality is not given in this post.
useRouteLoaderData() Hook

src\components\routes\PostEdit.js

This component retrieves the post data (by id) using useRouteLoaderData() hook and pass that data to another component PostForm as a prop.

import { useRouteLoaderData } from "react-router";
import PostForm from "./PostForm";

const PostEdit = () => {
  const postData = useRouteLoaderData("post-detail");
  return(
    <PostForm postData={postData} />
  )
}

export default PostEdit;
src\components\routes\PostForm.js
const PostForm = ({postData}) => {
  return(
    <div className="container">
      <h2>Post</h2>
      <form>
        <div className="mb-2 mt-2">
            <label className="form-label" htmlFor="id">ID: </label>
            <input className="form-control" type="text" name="id" id="id" defaultValue={postData?postData.id:""}></input>
        </div>
        <div className="mb-2">
            <label className="form-label" htmlFor="title">Title: </label>
            <input className="form-control" type="text" name="title" id="title" defaultValue={postData?postData.title:""}></input>
        </div>
        <div className="mb-2">
            <label className="form-label" htmlFor="body">Body: </label>
            <textarea className="form-control" type="text" name="body" id="body" defaultValue={postData?postData.body:""}></textarea>
        </div>
        <div className="mb-2">
            <label className="form-label" htmlFor="userId">User ID: </label>
            <input className="form-control" type="text" name="userId" id="userId" rows="3" defaultValue={postData?postData.userId:""}></input>
        </div>
        <button className="btn btn-info" type="submit">Save</button>
      </form>
    </div>
  )
}

export default PostForm;

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


Related Topics

  1. Lazy Loading Routes in React
  2. Data Loader in React Router
  3. Data Loader With Dynamic Routes in React
  4. React Declarative Approach

You may also like-

  1. JavaScript Rest Parameter
  2. Spring Boot Observability - Distributed Tracing, Metrics
  3. Java Record Class With Examples
  4. Interface in Java With Examples
  5. Angular CanActivateChild Guard to protect Child Routes

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