MENU
useLoaderData
useLoaderData() returns whatever the current route's loader function returned – or, if the loader was async and returned a Promise, whatever that Promise resolved to. React Router calls the loader and waits for it before rendering the route, so by the time a component calls useLoaderData(), the data is already sitting there; there's no loading state to manage and no risk of rendering before the data arrives.
Each route's data is scoped to that route: a component only sees the return value of the loader defined on the exact route it was rendered for, not a parent's or a child's. If a route doesn't define its own loader, useLoaderData() has nothing to return there.
import React from "react";
import { createBrowserRouter, Link, Outlet, useLoaderData } from "react-router";
import { RouterProvider } from "react-router/dom";
function Layout() {
return (
<div>
<ul>
<li><Link to="/">Pick a user</Link></li>
<li><Link to="/users/1">User 1</Link></li>
<li><Link to="/users/2">User 2</Link></li>
</ul>
<Outlet />
</div>
);
}
const router = createBrowserRouter([
{
path: "/",
element: <Layout />,
children: [
{ index: true, element: <h2>Pick a user above</h2> },
{
path: "users/:id",
element: <UserProfile />,
loader: async ({ params }) => {
let response = await fetch(`/api/users/${params.id}`);
return response.json();
},
},
],
},
]);
export default function UseLoaderDataExample() {
return <RouterProvider router={router} />;
}
function UserProfile() {
let user = useLoaderData();
return (
<div>
<h2>{user.name}</h2>
<p>{user.email}</p>
</div>
);
}Because the loader has already run by render time, useLoaderData() itself has no concept of a pending state. For that, see useNavigation, which reports whenever a loader (or action) is running for the next navigation, and useRevalidator, which lets you re-run the current route's loaders on demand.