Skip to content
Back to skills

Remix

ASecurity

Covers Remix / React Router v7: nested routes with parallel data loading, loaders and actions (server-side data fetching and mutations), progressive enhancement via <Form> (works without JavaScript), clientLoader/clientAction for client-side caching, defer() for streaming slow data with <Suspense> and <Await>, per-route ErrorBoundary and HydrateFallback, useFetcher for non-navigation mutations, Vite integration, route configuration (explicit routes.ts and file-system conventions), type genera...

  • 4 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added September 24, 2026
developmentjavascripttypescriptjavareactnodeexpresstestingapi

Works with

  • cli
  • api

Security analysis

A100/100

Pro scans all 9 files and shows the line behind each finding

Scanned September 24, 2026

npx -y skills add chrishuffman5/domain-expert --skill remix --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Remix?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Remix
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/chrishuffman5-remix/badge)](https://www.skillsdirectory.com/skills/chrishuffman5-remix)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: remix
description: "Covers Remix / React Router v7: nested routes with parallel data loading, loaders and actions (server-side data fetching and mutations), progressive enhancement via <Form> (works without JavaScript), clientLoader/clientAction for client-side caching, defer() for streaming slow data with <Suspense> and <Await>, per-route ErrorBoundary and HydrateFallback, useFetcher for non-navigation mutations, Vite integration, route configuration (explicit routes.ts and file-system conventions), type generation (Route.* types), deployment adapters (Node, Cloudflare, Express, Deno), and migration from Remix v2 to React Router v7. Use for \"Remix\", \"remix\", \"React Router v7\", \"react-router\", \"loader\", \"action Remix\", \"nested routes Remix\", \"useFetcher\", \"progressive enhancement Remix\" questions."
license: MIT
---

# Remix / React Router v7

This skill covers Remix / React Router v7. Remix v2 rebranded to React Router v7 in late 2024 -- same team, same runtime model, same API.

It covers:

- Nested routes: route tree, parallel loader execution, code splitting, `<Outlet />`
- Loaders: server-side GET data fetching, `params`, `request`, throwing Response for errors
- Actions: server-side POST/PUT/DELETE mutations, FormData, redirect after success (PRG)
- Progressive enhancement: `<Form>` works as plain HTML without JS; AJAX with JS
- Streaming: `defer()` with `<Suspense>` and `<Await>` for slow data
- Client loaders/actions: `clientLoader`, `clientAction`, `HydrateFallback`
- Error boundaries: per-route `ErrorBoundary`, `isRouteErrorResponse`, error isolation
- Data hooks: `useLoaderData`, `useActionData`, `useRouteLoaderData`, `useFetcher`, `useNavigation`
- Route configuration: explicit `routes.ts`, file-system conventions via `@react-router/fs-routes`
- Vite integration: `reactRouter()` plugin, HMR, code splitting, type generation
- Deployment adapters: Node.js, Cloudflare Workers, Express, Deno
- Migration: Remix v2 to React Router v7 (package renames, config, type generation)

## How to Approach Tasks

When you receive a request:

1. **Classify** the request type:
   - **Data loading** -- Load `references/patterns/data-loading.md`
   - **Forms / mutations** -- Load `references/patterns/forms.md`
   - **Migration** -- Load `references/patterns/migration.md`
   - **Architecture / Design** -- Load `references/architecture.md`
   - **Best practices** -- Load `references/best-practices.md`
   - **Troubleshooting** -- Load `references/diagnostics.md`
   - **Configuration** -- Reference `assets/react-router.config.ts` or `assets/routes.ts`

2. **Identify context** -- Determine if the user is on Remix v2 or React Router v7. The API is identical; only package names and config file names changed.

3. **Load context** -- Read the relevant reference file.

4. **Analyze** -- Apply Remix-specific reasoning. Remix favors Web standards (Request, Response, FormData, Headers). Avoid recommending client-side state management for data that belongs in loaders/actions.

5. **Recommend** -- Provide actionable guidance. Prefer `<Form>` over `fetch`, loaders over `useEffect`, and actions over client-side mutation.

6. **Verify** -- Suggest validation steps (Network tab, `useNavigation().state`, ErrorBoundary testing).

## Core Expertise

### Nested Routes

React Router v7 builds a route tree from `app/routes.ts`. Every route module can export `loader`, `action`, `ErrorBoundary`, `HydrateFallback`, and a default component. Parent layouts render `<Outlet />` for children.

```typescript
// app/routes.ts
import { type RouteConfig, route, index, layout, prefix } from "@react-router/dev/routes";

export default [
  index("routes/home.tsx"),
  route("about", "routes/about.tsx"),
  layout("routes/_auth.tsx", [
    route("login", "routes/login.tsx"),
    route("register", "routes/register.tsx"),
  ]),
  ...prefix("products", [
    index("routes/products.index.tsx"),
    route(":id", "routes/products.$id.tsx"),
  ]),
] satisfies RouteConfig;
```

Key behaviors: each route owns its own data, error, and loading state. A child error does not unmount siblings. All loaders on the matched branch fire simultaneously (parallel). Each route is a separate Vite chunk.

### Loaders (GET)

```typescript
export async function loader({ params, request }: LoaderFunctionArgs) {
  const product = await db.product.findUnique({ where: { id: params.id } });
  if (!product) throw new Response("Not Found", { status: 404 });
  return { product };
}

export default function ProductPage() {
  const { product } = useLoaderData<typeof loader>();
  return <h1>{product.name}</h1>;
}
```

### Actions (POST/PUT/DELETE)

```typescript
export async function action({ request, params }: ActionFunctionArgs) {
  const formData = await request.formData();
  const name = String(formData.get("name"));
  await db.product.update({ where: { id: params.id }, data: { name } });
  return redirect(`/products/${params.id}`);
}
```

### Progressive Enhancement

```tsx
<Form method="post">
  <input name="email" type="email" />
  <button type="submit">Subscribe</button>
</Form>
```

Without JS: plain HTML form POST. With JS: fetch-based, no full reload.

### Streaming with defer()

```typescript
export async function loader() {
  const fastData = await getFastData();
  const slowPromise = getSlowData();       // NOT awaited
  return defer({ fastData, slowPromise });
}

export default function Page() {
  const { fastData, slowPromise } = useLoaderData<typeof loader>();
  return (
    <>
      <h1>{fastData.title}</h1>
      <Suspense fallback={<p>Loading...</p>}>
        <Await resolve={slowPromise}>
          {(data) => <ReviewList reviews={data} />}
        </Await>
      </Suspense>
    </>
  );
}
```

### Per-Route Error Boundaries

```tsx
export function ErrorBoundary() {
  const error = useRouteError();
  if (isRouteErrorResponse(error)) {
    return <h1>{error.status} -- {error.data}</h1>;
  }
  if (error instanceof Error) return <p>{error.message}</p>;
  throw error;
}
```

### useFetcher (Non-Navigation Mutations)

```tsx
function LikeButton({ postId }: { postId: string }) {
  const fetcher = useFetcher();
  return (
    <fetcher.Form method="post" action={`/posts/${postId}/like`}>
      <button>{fetcher.state !== "idle" ? "Saving..." : "Like"}</button>
    </fetcher.Form>
  );
}
```

## Common Pitfalls

**1. Missing action export for POST forms**
`<Form method="post">` triggers the route's action. If no action is exported, you get 405 Method Not Allowed.

**2. Missing name attributes on inputs**
FormData requires `name` attributes. Unnamed inputs are not included.

**3. No redirect after successful action**
Always redirect after mutation (PRG pattern) to prevent double-submission on browser back.

**4. useLoaderData returns undefined**
Reading the wrong route's data. Use `useRouteLoaderData("routes/parent")` for parent data.

**5. defer values never resolve**
Missing `<Suspense>` + `<Await>` wrapper around deferred data.

**6. Root ErrorBoundary missing full HTML**
The root `ErrorBoundary` must render a complete `<html>` document since no layout wraps it.

**7. Stale data after mutation**
Loaders automatically re-run after actions on the same route. If data appears stale, check that the action returns a redirect.

**8. clientLoader.hydrate without HydrateFallback**
Setting `hydrate = true` without exporting `HydrateFallback` causes a flash of missing content.

## Reference Files

- `references/architecture.md` -- Nested routes, loaders/actions, progressive enhancement
- `references/best-practices.md` -- Data flow, form handling, error boundaries, deployment
- `references/diagnostics.md` -- Route errors, hydration issues, migration problems

## Configuration References

- `assets/react-router.config.ts` -- Annotated config
- `assets/routes.ts` -- Route configuration patterns

## Pattern Guides

- `references/patterns/data-loading.md` -- Loaders, defer, clientLoader
- `references/patterns/forms.md` -- Actions, Zod validation, progressive enhancement
- `references/patterns/migration.md` -- Remix v2 to React Router v7

Files in this skill

  • SKILL.md7.9 KB
  • assets/react-router.config.ts1.1 KB
  • assets/routes.ts2.3 KB
  • references/architecture.md5 KB
  • references/best-practices.md4 KB
  • references/diagnostics.md3.1 KB
  • references/patterns/data-loading.md3.6 KB
  • references/patterns/forms.md3.6 KB
  • references/patterns/migration.md3.5 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…