# React Router

> Add or extend client-side routing with React Router v7 in Data Mode (createBrowserRouter + RouterProvider), wired to vertical slices, TanStack Query, and zod-parsed URL state.

- Skill: `thibautbaissac/react-router` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add thibautbaissac/react-router`
- Raw SKILL.md: https://api.skillmd.com/api/skills/thibautbaissac/react-router/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Web & Frontend
- Author: thibautbaissac (https://skillmd.com/u/thibautbaissac)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/thibautbaissac/react-router

---


# Add React Router v7 routing

Add `$route` to the Vite SPA using React Router v7 **Data Mode**:
`createBrowserRouter` + `RouterProvider`, imported from `react-router`.
TanStack Query keeps server-state ownership. Do not adopt Framework/SSR or bare
Declarative Mode; Express remains the backend.

## Step 1 — Detect the toolchain and ensure the dep

```bash
source "${CLAUDE_PROJECT_DIR}/.claude/lib/detect-toolchain.sh"
```

If missing, add `react-router` with the detected package manager. Do not add
`react-router-dom`; v7 uses one package. Confirm major version 7.

## Step 2 — Place routing inside the feature slices

Keep frontend routing in vertical slices, never in backend `routes/`.
- One composition root, e.g. `app/router.tsx`, calls `createBrowserRouter` and is rendered by `<RouterProvider>` at the app entry.
- Each feature exports its own route objects (e.g. `features/invoice/invoice.routes.tsx`) that the root composes.
  A feature owns its routes like it owns its components and hooks.

## Step 3 — Define routes with lazy code-splitting

Apply `${CLAUDE_SKILL_DIR}/references/patterns.md` sections "Router setup" and
"Lazy routes".
- Use route objects with `Component`, nested `children`, `index`, and layout routes that render `<Outlet/>`.
- Split each feature's route with `lazy` so its bundle loads on navigation.

## Step 4 — Keep TanStack Query the source of truth for server data

Apply the reference section "TanStack Query integration".
- Components keep fetching through their `useQuery`/`useMutation` hooks.
  Do not re-fetch the same server data inside a `loader` and return it as a parallel source of truth.
- If a route needs to prefetch, have its `loader` call `queryClient.ensureQueryData(...)` for the feature's existing query key, then let the component read it via the same `useQuery` (warm cache).
- Use `action` only for non-Query mutations or progressive-enhancement forms; otherwise mutate via the existing TanStack Query mutation hook and invalidate keys.

## Step 5 — Parse URL and search params at the boundary

Apply the reference section "Typed params". Parse untrusted `params` and
`useSearchParams` with zod. Reuse an existing feature contract when it fits.

## Step 6 — Pending and error states

Surface pending state with `useNavigation`. Give routes an `ErrorBoundary`
(`ErrorBoundary`/`errorElement`) using `useRouteError`.

## Step 7 — Verify

```bash
source "${CLAUDE_PROJECT_DIR}/.claude/lib/detect-toolchain.sh"
run_typecheck
```

## Checklist

- [ ] Data Mode only: `createBrowserRouter` + `RouterProvider`, imports from `react-router` (v7).
- [ ] No Framework/SSR adoption; Express stays the backend.
- [ ] Routes live in feature slices on the frontend, composed by one root; not under backend `routes/`.
- [ ] Server state still owned by TanStack Query; loaders at most prefetch into the query client.
- [ ] `params`/search params parsed with zod into typed values.
- [ ] Routes are lazy-split and have pending + error states.
- [ ] `run_typecheck` passes.

