The files referenced in routes.ts
are called Route Modules.
route("teams/:teamId", "./team.tsx"),
// route module ^^^^^^^^
Route modules are the foundation of React Router's framework features, they define:
This guide is a quick overview of every route module feature. The rest of the getting started guides will cover these features in more detail.
default
)The default
export in a route module defines the component that will render when the route matches.
export default function MyRouteComponent() {
return (
<div>
<h1>Look ma!</h1>
<p>
I'm still using React Router after like 10 years.
</p>
</div>
);
}
When the component is rendered, it is provided the props defined in Route.ComponentProps
that React Router will automatically generate for you. These props include:
loaderData
: The data returned from the loader
function in this route moduleactionData
: The data returned from the action
function in this route moduleparams
: An object containing the route parameters (if any).matches
: An array of all the matches in the current route tree.You can use these props in place of hooks like useLoaderData
or useParams
. This may be preferrable because they will be automatically typed correctly for the route.
import type { Route } from "./+types/route-name";
export default function MyRouteComponent({
loaderData,
actionData,
params,
matches,
}: Route.ComponentProps) {
return (
<div>
<h1>Welcome to My Route with Props!</h1>
<p>Loader Data: {JSON.stringify(loaderData)}</p>
<p>Action Data: {JSON.stringify(actionData)}</p>
<p>Route Parameters: {JSON.stringify(params)}</p>
<p>Matched Routes: {JSON.stringify(matches)}</p>
</div>
);
}
loader
Route loaders provide data to route components before they are rendered. They are only called on the server when server rendering or during the build with pre-rendering.
export async function loader() {
return { message: "Hello, world!" };
}
export default function MyRoute({ loaderData }) {
return <h1>{loaderData.message}</h1>;
}
See also:
clientLoader
Called only in the browser, route client loaders provide data to route components in addition to, or in place of, route loaders.
export async function clientLoader({ serverLoader }) {
// call the server loader
const serverData = await serverLoader();
// And/or fetch data on the client
const data = getDataFromClient();
// Return the data to expose through useLoaderData()
return data;
}
Client loaders can participate in initial page load hydration of server rendered pages by setting the hydrate
property on the function:
export async function clientLoader() {
// ...
}
clientLoader.hydrate = true as const;
By using as const
, TypeScript will infer that the type for clientLoader.hydrate
is true
instead of boolean
.
That way, React Router can derive types for loaderData
based on the value of clientLoader.hydrate
.
See also:
action
Route actions allow server-side data mutations with automatic revalidation of all loader data on the page when called from <Form>
, useFetcher
, and useSubmit
.
// route("/list", "./list.tsx")
import { Form } from "react-router";
import { TodoList } from "~/components/TodoList";
// this data will be loaded after the action completes...
export async function loader() {
const items = await fakeDb.getItems();
return { items };
}
// ...so that the list here is updated automatically
export default function Items({ loaderData }) {
return (
<div>
<List items={loaderData.items} />
<Form method="post" navigate={false} action="/list">
<input type="text" name="title" />
<button type="submit">Create Todo</button>
</Form>
</div>
);
}
export async function action({ request }) {
const data = await request.formData();
const todo = await fakeDb.addItem({
title: data.get("title"),
});
return { ok: true };
}
clientAction
Like route actions but only called in the browser.
export async function clientAction({ serverAction }) {
fakeInvalidateClientSideCache();
// can still call the server action if needed
const data = await serverAction();
return data;
}
See also:
ErrorBoundary
When other route module APIs throw, the route module ErrorBoundary
will render instead of the route component.
import {
isRouteErrorResponse,
useRouteError,
} from "react-router";
export function ErrorBoundary() {
const error = useRouteError();
if (isRouteErrorResponse(error)) {
return (
<div>
<h1>
{error.status} {error.statusText}
</h1>
<p>{error.data}</p>
</div>
);
} else if (error instanceof Error) {
return (
<div>
<h1>Error</h1>
<p>{error.message}</p>
<p>The stack trace is:</p>
<pre>{error.stack}</pre>
</div>
);
} else {
return <h1>Unknown Error</h1>;
}
}
HydrateFallback
On initial page load, the route component renders only after the client loader is finished. If exported, a HydrateFallback
can render immediately in place of the route component.
export async function clientLoader() {
const data = await fakeLoadLocalGameData();
return data;
}
export function HydrateFallback() {
return <p>Loading Game...</p>;
}
export default function Component({ loaderData }) {
return <Game data={loaderData} />;
}
headers
Route headers define HTTP headers to be sent with the response when server rendering.
export function headers() {
return {
"X-Stretchy-Pants": "its for fun",
"Cache-Control": "max-age=300, s-maxage=3600",
};
}
handle
Route handle allows apps to add anything to a route match in useMatches
to create abstractions (like breadcrumbs, etc.).
export const handle = {
its: "all yours",
};
links
Route links define <link>
elements to be rendered in the document <head>
.
export function links() {
return [
{
rel: "icon",
href: "/favicon.png",
type: "image/png",
},
{
rel: "stylesheet",
href: "https://example.com/some/styles.css",
},
{
rel: "preload",
href: "/images/banner.jpg",
as: "image",
},
];
}
All routes links will be aggregated and rendered through the <Links />
component, usually rendered in your app root:
import { Links } from "react-router";
export default function Root() {
return (
<html>
<head>
<Links />
</head>
<body />
</html>
);
}
meta
Route meta defines meta tags to be rendered in the <head>
of the document.
export function meta() {
return [
{ title: "Very cool app" },
{
property: "og:title",
content: "Very cool app",
},
{
name: "description",
content: "This app is the best",
},
];
}
All routes' meta will be aggregated and rendered through the <Meta />
component, usually rendered in your app root:
import { Meta } from "react-router";
export default function Root() {
return (
<html>
<head>
<Meta />
</head>
<body />
</html>
);
}
See also
shouldRevalidate
By default, all routes are revalidated after actions. This function allows a route to opt-out of revalidation for actions that don't affect its data.
import type { ShouldRevalidateFunctionArgs } from "react-router";
export function shouldRevalidate(
arg: ShouldRevalidateFunctionArgs
) {
return true;
}
Next: Rendering Strategies