MENU
useOutletContext
A layout route's element often holds state or other values that its nested child route wants to read or update. You could wire up your own React Context for that, but <Outlet> already has a spot for it built in: pass a context prop to <Outlet>, and whichever child route is currently rendered inside it can read that value back with useOutletContext().
Unlike useFetcher(), useSubmit(), or useRouteError(), useOutletContext() doesn't actually require a data router – <Outlet context> works the same way underneath a plain <BrowserRouter><Routes> tree too. It's covered in this section because it's most often reached for alongside the other data-loading hooks in a nested layout route, as in the createBrowserRouter example below.
import React, { useState } from "react";
import { createBrowserRouter, Link, Outlet, useOutletContext } from "react-router";
import { RouterProvider } from "react-router/dom";
const router = createBrowserRouter([
{
path: "/",
element: <Layout />,
children: [
{ index: true, element: <Home /> },
{ path: "counter", element: <Counter /> },
],
},
]);
export default function UseOutletContextExample() {
return <RouterProvider router={router} />;
}
function Layout() {
let [count, setCount] = useState(0);
return (
<div>
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/counter">Counter</Link></li>
</ul>
<p>Count, tracked by the layout route: {count}</p>
<Outlet context={{ count, setCount }} />
</div>
);
}
function Home() {
return <h3>Pick "Counter" to read and update the layout route's state.</h3>;
}
function Counter() {
let { count, setCount } = useOutletContext();
return (
<button onClick={() => setCount((c) => c + 1)}>
Clicked from the child route {count} times
</button>
);
}Layout renders once and owns the count state; swapping between Home and Counter only changes what <Outlet> renders underneath it, so that state survives the navigation. If you're using TypeScript, a common trick is to have the layout route also export its own custom hook (e.g. useCount(), itself just calling useOutletContext<ContextType>()) so child routes get proper typing without repeating the type parameter everywhere.