Documentation Index
Fetch the complete documentation index at: https://mintlify.com/remix-run/react-router/llms.txt
Use this file to discover all available pages before exploring further.
default export
The React component that renders when the route matches.
Signature
export default function Component() {
// Component implementation
}
The default export is a React component with no props. It must be present in every route module.
Basic Example
// app/routes/about.tsx
export default function About() {
return (
<div>
<h1>About Us</h1>
<p>We build amazing software.</p>
</div>
);
}
Using Loader Data
import { useLoaderData } from "react-router";
export async function loader({ params }: Route.LoaderArgs) {
const product = await fetchProduct(params.productId);
return { product };
}
export default function Product() {
const { product } = useLoaderData<typeof loader>();
return (
<div>
<h1>{product.name}</h1>
<p>{product.description}</p>
<p>${product.price}</p>
</div>
);
}
Nested Routes with Outlet
import { Outlet } from "react-router";
export default function Layout() {
return (
<div>
<header>
<nav>{/* navigation */}</nav>
</header>
<main>
{/* Child routes render here */}
<Outlet />
</main>
<footer>{/* footer */}</footer>
</div>
);
}
import { Form, useActionData } from "react-router";
export async function action({ request }: Route.ActionArgs) {
const formData = await request.formData();
const email = formData.get("email");
if (!email?.includes("@")) {
return { error: "Invalid email" };
}
await subscribe(email);
return { success: true };
}
export default function Newsletter() {
const actionData = useActionData<typeof action>();
return (
<div>
<h2>Subscribe to our newsletter</h2>
{actionData?.success ? (
<p>Thanks for subscribing!</p>
) : (
<Form method="post">
<input type="email" name="email" required />
{actionData?.error && (
<span className="error">{actionData.error}</span>
)}
<button type="submit">Subscribe</button>
</Form>
)}
</div>
);
}
Navigation and Params
import { useParams, useNavigate, Link } from "react-router";
export default function Team() {
const params = useParams();
const navigate = useNavigate();
return (
<div>
<h1>Team: {params.teamId}</h1>
<nav>
<Link to="members">Members</Link>
<Link to="settings">Settings</Link>
</nav>
<button onClick={() => navigate("/teams")}>
Back to Teams
</button>
</div>
);
}
Async Components
Components cannot be async. Use loaders for data fetching:
// ❌ This doesn't work
export default async function Product() {
const product = await fetchProduct();
return <div>{product.name}</div>;
}
// ✅ Use a loader instead
export async function loader() {
const product = await fetchProduct();
return { product };
}
export default function Product() {
const { product } = useLoaderData<typeof loader>();
return <div>{product.name}</div>;
}
Component Naming
You can use any valid function name:
// All of these work
export default function Component() { /* ... */ }
export default function ProductDetail() { /* ... */ }
export default function() { /* ... */ }
// Arrow functions work too
const Component = () => { /* ... */ };
export default Component;
Best Practices
Structure your components with proper HTML elements:export default function Article() {
return (
<article>
<header>
<h1>Title</h1>
<time>2024-01-01</time>
</header>
<section>{/* content */}</section>
<footer>{/* metadata */}</footer>
</article>
);
}
Extract complex UI into separate components:// app/routes/dashboard.tsx
import { Sidebar } from "~/components/Sidebar";
import { MetricsPanel } from "~/components/MetricsPanel";
export default function Dashboard() {
return (
<div className="dashboard">
<Sidebar />
<MetricsPanel />
</div>
);
}
Handle loading and error states
Use hooks to handle different states:import { useNavigation } from "react-router";
export default function SearchResults() {
const navigation = useNavigation();
const data = useLoaderData<typeof loader>();
const isSearching = navigation.state === "loading";
return (
<div>
{isSearching && <Spinner />}
<ul>
{data.results.map(result => (
<li key={result.id}>{result.name}</li>
))}
</ul>
</div>
);
}
Use TypeScript for type safety
Leverage type inference from loaders and actions:export async function loader() {
return { message: "Hello", count: 42 };
}
export default function Component() {
const data = useLoaderData<typeof loader>();
data.message; // string
data.count; // number
}
See Also