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
)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>
);
}
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 pages loads, 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 { Route } from "./+types/my-route";
export function shouldRevalidate(
arg: Route.ShouldRevalidateArg
) {
return true;
}
Next: Rendering Strategies