React Router V7

Data & APIs

React Router v7 best practices for data-driven routing. Use when implementing routes, loaders, actions, Form components, fetchers, navigation guards, protected routes, or URL search params. Triggers on createBrowserRouter, RouterProvider, useLoaderData, useActionData, useFetcher, NavLink, Outlet.

Install

openclaw skills install react-router-v7

React Router v7 Best Practices

Quick Reference

Router Setup (Data Mode):

import { createBrowserRouter, RouterProvider } from "react-router";

const router = createBrowserRouter([
  {
    path: "/",
    Component: Root,
    ErrorBoundary: RootErrorBoundary,
    loader: rootLoader,
    children: [
      { index: true, Component: Home },
      { path: "products/:productId", Component: Product, loader: productLoader },
    ],
  },
]);

ReactDOM.createRoot(root).render(<RouterProvider router={router} />);

Framework Mode (Vite plugin):

// routes.ts
import { index, route } from "@react-router/dev/routes";

export default [
  index("./home.tsx"),
  route("products/:pid", "./product.tsx"),
];

Route Configuration

Nested Routes with Outlets

createBrowserRouter([
  {
    path: "/dashboard",
    Component: Dashboard,
    children: [
      { index: true, Component: DashboardHome },
      { path: "settings", Component: Settings },
    ],
  },
]);

function Dashboard() {
  return (
    <div>
      <h1>Dashboard</h1>
      <Outlet /> {/* Renders child routes */}
    </div>
  );
}

Dynamic Segments and Splats

{ path: "teams/:teamId" }           // params.teamId
{ path: ":lang?/categories" }       // Optional segment
{ path: "files/*" }                 // Splat: params["*"]

Key Decision Points

Form vs Fetcher

Use <Form>: Creating/deleting with URL change, adding to history Use useFetcher: Inline updates, list operations, popovers - no URL change

Loader vs useEffect

Use loader: Data before render, server-side fetch, automatic revalidation Use useEffect: Client-only data, user-interaction dependent, subscriptions

Gates (decision sequencing)

Answer in order. Pass means the condition is true; pick the API on the same line and stop.

<Form> vs useFetcher

  1. Must the URL or history stack change (bookmark/share, back returns to prior screen)?
    • Pass → <Form> / route action (or useSubmit + navigation). Stop.
    • Fail → Step 2.
  2. Mutation stays on the same route (inline edit, modal, list row, no address change)?
    • Pass → useFetcher(). Stop.
    • Fail → Re-check step 1; you may need a dedicated action route or POST to the current URL.

loader vs useEffect

  1. Is data needed for correct first render (or your intended <Suspense> boundary) for this route?
    • Pass → loader (Framework: clientLoader when appropriate). Stop.
    • Fail → Step 2.
  2. Fetch only after mount from user action, timer, or subscription (not route entry)?
    • Pass → useEffect / event handlers. Stop.
    • Fail → Prefer loader + revalidation over an effect that mirrors navigation.

Additional Documentation

Mode Comparison

FeatureFramework ModeData ModeDeclarative Mode
SetupVite plugincreateBrowserRouter<BrowserRouter>
Type SafetyAuto-generated typesManualManual
SSR SupportBuilt-inManualLimited
Use CaseFull-stack appsSPAs with controlSimple/legacy