Check your version

This post assumes you're using React Router v6. If not, find your version below.

As of today, React Router v6 doesn't ship with support for preventing transitions. Once this issue is resolved, we'll update this post with the recommended way to prevent transitions in your app.

For now, here's a very hacky not "approved" approach that "works".

import * as React from "react";
import { UNSAFE_NavigationContext as NavigationContext } from "react-router-dom";
export function useBlocker(blocker, when = true) {
const { navigator } = React.useContext(NavigationContext);
React.useEffect(() => {
if (!when) return;
const unblock = navigator.block((tx) => {
const autoUnblockingTx = {
...tx,
retry() {
unblock();
tx.retry();
},
};
blocker(autoUnblockingTx);
});
return unblock;
}, [navigator, blocker, when]);
}
export default function usePrompt(message, when = true) {
const blocker = React.useCallback(
(tx) => {
if (window.confirm(message)) tx.retry();
},
[message]
);
useBlocker(blocker, when);
}

Now you can usePrompt in your app. usePrompt receives two arguments – when and message. when is a boolean that if true, will show the user a prompt with the message when they try to navigate away.

function Form() {
const [name, setName] = React.useState("");
const [email, setEmail] = React.useState("");
const [note, setNote] = React.useState("");
const isDirty = () => {
return name.length > 0 || email.length > 0 || note.length > 0;
};
usePrompt("Are you sure you want to leave?", isDirty());
return (
<form
onSubmit={(e) => {
e.preventDefault();
alert("Submitted!");
setName("");
setEmail("");
setNote("");
}}
>
...
</form>
);
}

Here's a Codesandbox with the above implementation.

Want to learn more?

If you liked this post and want to learn more, check out our free Comprehensive Guide to React Router.