DEV Community

Cover image for Custom React Router Prompt
Yadhunandan S
Yadhunandan S

Posted on

6

Custom React Router Prompt

How to implement the custom prompt on navigation using react-router


What’s the need for custom prompt ?

Most of the react projects follow the UI design system for their components. In order to achieve the same design consistency across the app, they reuse the components. However to block the navigation in case there are some unsaved user data, default browser prompt is the most common and easy way to block the user from navigating to different routes. But the popup does not match your library component.

There is no official docs explicitly mentioning how to use the custom modal instead of the default react-router Prompt.

Solution

There are few simple steps to be followed in order to get the required result. Each step contains 100% working code snippets. You can use it in your projects.

Step 1

We need to disable or block the default browser prompt. This can be done by passing an empty callback to the getUserConfirmation function prop of the BrowserRouter.

import { BrowserRouter, Route, Switch } from "react-router-dom";
import { Home } from "./Home";
import { Dashboard } from "./Dashboard";
import "./App.css";
function App() {
return (
<div className="App">
{/* Check the line no 13 */}
<BrowserRouter
getUserConfirmation={() => {
/* Empty callback to block the default browser prompt */
}}
>
<Switch>
<Route path="/dashboard">
<Dashboard />
</Route>
<Route path="/">
<Home />
</Route>
</Switch>
</BrowserRouter>
</div>
);
}
export default App;
view raw App.jsx hosted with ❤ by GitHub

Step 2

Now let’s create a custom RouterPrompt component using the useHistory hook from the react-router-dom and ant design component library for the Modal component.

import React, { useCallback, useEffect, useState } from "react";
import { useHistory } from "react-router";
import { Modal } from "antd";
export function RouterPrompt(props) {
const { when, onOK, onCancel, title, okText, cancelText } = props;
const history = useHistory();
const [showPrompt, setShowPrompt] = useState(false);
const [currentPath, setCurrentPath] = useState("");
useEffect(() => {
if (when) {
history.block((prompt) => {
setCurrentPath(prompt.pathname);
setShowPrompt(true);
return "true";
});
} else {
history.block(() => {});
}
return () => {
history.block(() => {});
};
}, [history, when]);
const handleOK = useCallback(async () => {
if (onOK) {
const canRoute = await Promise.resolve(onOK());
if (canRoute) {
history.block(() => {});
history.push(currentPath);
}
}
}, [currentPath, history, onOK]);
const handleCancel = useCallback(async () => {
if (onCancel) {
const canRoute = await Promise.resolve(onCancel());
if (canRoute) {
history.block(() => {});
history.push(currentPath);
}
}
setShowPrompt(false);
}, [currentPath, history, onCancel]);
return showPrompt ? (
<Modal
title={title}
visible={showPrompt}
onOk={handleOK}
okText={okText}
onCancel={handleCancel}
cancelText={cancelText}
closable={true}
>
There are unsaved changes. Are you sure want to leave this page ?
</Modal>
) : null;
}

Step 3

Let’s create two sample components for navigating between pages. For example, let the first component be Dashboard.jsx and the second component be Home.jsx.

import { useHistory } from "react-router-dom";
export function Dashboard() {
const history = useHistory();
const handleRoute = () => {
history.push("/home");
};
return (
<div>
<div>Dashboard component</div>
<button onClick={handleRoute}>Go to home</button>
</div>
);
}
view raw Dashboard.jsx hosted with ❤ by GitHub

The home component contains our custom RouterPrompt component. The prompt is triggered on the page change and also based on the when prop. The when prop is used to conditionally trigger the prompt. For instance, if the when prop is set to true , the prompt will be shown every time page navigates (from home route to other routes or navigate forward / back).

import { useState } from "react";
import { useHistory } from "react-router-dom";
import { RouterPrompt } from "./RouterPrompt";
export function Home() {
const history = useHistory();
const [showPrompt, setShowPrompt] = useState(false);
const [inputValue, setInputValue] = useState("");
const handleRoute = () => {
if (inputValue && inputValue.length > 0) {
setShowPrompt(true);
}
// Since state update is async, we need the showPrompt state to be
// updated first in order to show the prompt
setImmediate(() => history.push("/dashboard"));
};
return (
<>
<RouterPrompt
when={showPrompt}
title="Leave this page"
cancelText="Cancel"
okText="Confirm"
onOK={() => true}
onCancel={() => false}
/>
<div className="inputBox">
<input onChange={(evt) => setInputValue(evt.target.value)} />
</div>
<button onClick={handleRoute}>Go to dashboard</button>
</>
);
}
view raw Home.jsx hosted with ❤ by GitHub

The onOK and onCancel callback props must return boolean value in order to proceed with the route.

Result

Result

Conclusion

You can use any component library’s modal, even your own custom modal or default browser prompt. But just be careful when implementing the history.block.

By following the above steps you can achieve the desired solution. The code is 100% working. You can copy the code snippets to your applications to save time.

References

  1. https://reactrouter.com/web/api/Hooks/usehistory
  2. https://reactrouter.com/web/api/history

Hostinger image

Get n8n VPS hosting 3x cheaper than a cloud solution

Get fast, easy, secure n8n VPS hosting from $4.99/mo at Hostinger. Automate any workflow using a pre-installed n8n application and no-code customization.

Start now

Top comments (4)

Collapse
 
emrealparslan93 profile image
Emre Alparslan

Very helpful article. Thank you.

Collapse
 
yadhus profile image
Yadhunandan S

Thanks for the feedback!

Collapse
 
aditya_kumar_129 profile image
Aditya Kumar

Bro Your codes are using the components that are available in V5 such as useHistory, Switches etc
These are not available in V6

Collapse
 
yadhus profile image
Yadhunandan S

Yes, V6 still not supporting the router prompt. I am working on it to provide a solution soon.
reactrouter.com/docs/en/v6/upgradi...

Qodo Takeover

Introducing Qodo Gen 1.0: Transform Your Workflow with Agentic AI

Rather than just generating snippets, our agents understand your entire project context, can make decisions, use tools, and carry out tasks autonomously.

Read full post

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay