MENU
Preventing Transitions
Sometimes you want to prevent the user from navigating away from a page. The most common use case is when they have entered some data into a form but haven't submitted it yet, and you don't want them to lose it.
The <Prompt> component is gone, and its replacement, useBlocker(), only works underneath a data router (createBrowserRouter + <RouterProvider>) – it does not work inside a plain <BrowserRouter><Routes> tree. useBlocker() takes a boolean (or a function returning one) and gives back a "blocker" object you build your own confirmation UI around, with a "state" ("unblocked" / "blocked" / "proceeding"), and proceed()/reset() methods.
import React, { useState } from "react";
import { createBrowserRouter, Link, Outlet, useBlocker } from "react-router";
import { RouterProvider } from "react-router/dom";
function Layout() {
return (
<div>
<ul>
<li><Link to="/">Form</Link></li>
<li><Link to="/one">One</Link></li>
<li><Link to="/two">Two</Link></li>
</ul>
<Outlet />
</div>
);
}
const router = createBrowserRouter([
{
path: "/",
element: <Layout />,
children: [
{ index: true, element: <BlockingForm /> },
{ path: "one", element: <h3>One</h3> },
{ path: "two", element: <h3>Two</h3> },
],
},
]);
export default function PreventingTransitionsExample() {
return <RouterProvider router={router} />;
}
function BlockingForm() {
let [isBlocking, setIsBlocking] = useState(false);
let blocker = useBlocker(isBlocking);
return (
<form
onSubmit={(event) => {
event.preventDefault();
event.target.reset();
setIsBlocking(false);
}}
>
<p>
Blocking?{" "}
{isBlocking ? "Yes, click a link or the back button" : "Nope"}
</p>
<p>
<input
size="50"
placeholder="type something to block transitions"
onChange={(event) => { setIsBlocking(event.target.value.length > 0); }}
/>
</p>
<p><button>Submit to stop blocking</button></p>
{blocker.state === "blocked" && (
<p style={{ color: "red" }}>
Are you sure you want to leave?{" "}
<button type="button" onClick={() => blocker.proceed()}>Yes, leave</button>{" "}
<button type="button" onClick={() => blocker.reset()}>No, stay</button>
</p>
)}
</form>
);
}If you just want a plain window.confirm() dialog instead of building custom UI, there's also unstable_usePrompt({ when, message }), a thin convenience wrapper around useBlocker(). It's intentionally kept prefixed "unstable_" permanently – the browser's back/forward buttons can behave inconsistently across browsers while a confirm() dialog is open – and it has the same data-router requirement as useBlocker() itself.