本文へ移動
cccskills
無料GitHub で公開

web-meta-framework-remix

File-based routing, loaders, actions, defer streaming, useFetcher, error boundaries, progressive enhancement

インストール方法を見る

含まれるファイル(14)

  • SKILL.md14.5 KB
  • examples/actions.md6.0 KB
  • examples/core.md2.8 KB
  • examples/deferred.md3.4 KB
  • examples/error-handling.md4.2 KB
  • examples/forms.md5.7 KB
  • examples/loaders.md3.7 KB
  • examples/meta.md3.6 KB
  • examples/nested-routes.md5.3 KB
  • examples/optimistic.md5.0 KB
  • examples/react-router-v7.md13.9 KB
  • examples/resource-routes.md5.8 KB
  • metadata.yaml333 B
  • reference.md8.0 KB

SKILL.md(原文)

インストールする前に、エージェントに与えられる指示の中身を確認できます。

Remix / React Router v7 Framework Patterns

Quick Guide: A route exports a loader for reads and an action for writes, both server-only, so a component never fetches its own data. Forms submit without JavaScript and nested routes load in parallel. The version fact that changes every example below: React Router v7 deprecates json() and defer() — return raw objects and raw Promises, and use data() only when you need a custom status or header.

Detailed Resources:


Which path applies

  • On Remix v2 — the examples in this skill are written for it: @remix-run/* imports, json(), defer() and useLoaderData<typeof loader>().
  • On React Router v7 framework mode — the concepts carry over unchanged and the API does not. Read examples/react-router-v7.md first and translate as you go; the mapping table is below.
  • Building a page — a loader, a default export, and an ErrorBoundary; follow examples/core.md.
  • Building an endpoint — omit the default export and the route becomes a resource route; follow examples/resource-routes.md.

<migration_notice>

Remix v2 to React Router v7

Remix has merged into React Router v7. What was planned as Remix v3 is React Router v7's "framework mode".

Remix v2React Router v7
json(data)Return the raw object
json(data, { status, headers })data(data, { status, headers })
defer({ key: promise })Return { key: promise } — Single Fetch streams it
@remix-run/node importsreact-router / @react-router/node
LoaderFunctionArgsRoute.LoaderArgs (generated)
ActionFunctionArgsRoute.ActionArgs (generated)
useLoaderData<typeof loader>()loaderData from Route.ComponentProps
RemixServerServerRouter
RemixBrowserHydratedRouter (from react-router/dom)
File-based routing by defaultroutes.ts, with @react-router/fs-routes optional

Migration guide: Upgrading from Remix

</migration_notice>


<critical_requirements>

Before writing Remix code

Export loader and action from route modules only. The build wires them up by route, so the same export in a helper file is dead code that silently never runs.

Throw a Response for an expected failure — 404, 403 — and let the ErrorBoundary render it. Returning null instead pushes a null check onto every consumer and loses the status code.

Await the data the page cannot render without, and return the rest as Promises. Anything awaited delays the first byte; anything returned as a Promise streams in behind it.

Name HTTP status codes as constants. HTTP_NOT_FOUND says what the branch is for, where 404 has to be recognised.

</critical_requirements>


Auto-detection: loader, action, clientLoader, clientAction, useLoaderData, useActionData, useFetcher, useNavigation, useRouteError, isRouteErrorResponse, ErrorBoundary, HydrateFallback, shouldRevalidate, defer, Await, Outlet, NavLink, meta function, links function, @remix-run/node, @remix-run/react, react-router, ServerRouter, HydratedRouter, Route.LoaderArgs, Route.ComponentProps, routes.ts, Single Fetch, app/routes/

Applies to:

  • File-based routing, including nested layouts, index routes and pathless layouts
  • Server-side data loading in loaders, and mutations in actions
  • Forms that work before hydration and better after it
  • Streaming non-critical data behind Suspense and Await
  • Non-navigating mutations, optimistic UI and debounced search with useFetcher
  • Route-scoped error boundaries that branch on HTTP status
  • SEO through meta, and stylesheets and preloads through links
  • Resource routes: JSON APIs, webhooks, file downloads

Handled elsewhere:

  • Persistence — a loader queries and an action writes; neither the client nor the query shape is settled here
  • Session and password handling — this skill's examples call an auth layer and read what it returns
  • Schema validation — an action parses FormData; which library defines the schema is a separate choice
  • Styling — links returns stylesheet descriptors, and what is in them is someone else's concern
  • React component authoring itself, outside the route module's own exports

<philosophy>

Philosophy

Remix collapses full-stack development to one mental model: a route exports a loader for reads and an action for writes, and both run only on the server. That is what lets a route query a database directly without a secret reaching the browser, and it is why there is no client-side fetching library in the picture.

Four things follow:

  1. No fetch waterfalls — loaders run before the component renders, and nested loaders run in parallel with each other rather than in sequence down the tree
  2. Progressive enhancement is the default — a <Form> is a real form; JavaScript makes the submission smoother rather than making it possible
  3. HTTP semantics rather than framework ones — caching is Cache-Control, errors are status codes, and requests and responses are the platform's own objects
  4. The URL is the state — a nested URL maps to a nested component tree, and search params are where filter and pagination state lives
URL change -> loaders run in parallel -> component renders -> user submits
                                                                  |
                                              action runs -> loaders revalidate
</philosophy>
<patterns>

Core patterns

Pattern 1: File-Based Routing

Files in app/routes/ become URLs; the naming characters control nesting and dynamic segments.

FileURLWhat the name does
_index.tsx/Index route
about.tsx/aboutStatic segment
blog.$slug.tsx/blog/:slug$ marks a dynamic parameter
blog_.tsx/blogTrailing _ escapes the parent layout
_auth.tsxnoneLeading _ makes a pathless layout
_auth.login.tsx/loginNested inside that layout
$.tsx/*Splat / catch-all
// app/routes/blog.$slug.tsx
const HTTP_NOT_FOUND = 404;

export async function loader({ params }: LoaderFunctionArgs) {
  const post = await getPostBySlug(params.slug);
  if (!post) throw new Response("Not Found", { status: HTTP_NOT_FOUND });
  return { post };
}

Full code: examples/core.md and examples/nested-routes.md

Pattern 2: Loaders

A loader runs on the server for the initial render and over fetch on every client navigation afterwards.

export async function loader({ params, request }: LoaderFunctionArgs) {
  const user = await getUser(params.userId);
  if (!user) {
    throw json({ message: "User not found" }, { status: HTTP_NOT_FOUND });
  }
  return json({ user });
}

Parent loaders re-run when a child route changes — shouldRevalidate is the opt-out.

Full code: examples/loaders.md

Pattern 3: Actions

An action handles every non-GET method, runs before the loaders, and the loaders revalidate after it.

export async function action({ request }: ActionFunctionArgs) {
  const formData = await request.formData();

  switch (formData.get("intent")) {
    case "update":
      return json({ success: true });
    case "delete":
      return redirect("/items");
    default:
      throw new Error(`Unknown intent`);
  }
}

A hidden intent field is how one route serves several forms. Redirect after a successful mutation so a refresh does not resubmit.

Full code: examples/actions.md and examples/forms.md

Pattern 4: Streaming

Await what the page needs; hand back the rest as Promises.

// Remix v2
return defer({ user, analytics: getAnalytics() });

// React Router v7 — Single Fetch streams a raw Promise
return { user, analytics: getAnalytics() };
<Suspense fallback={<Skeleton />}>
  <Await resolve={analytics} errorElement={<p>Failed to load</p>}>
    {(data) => <Chart data={data} />}
  </Await>
</Suspense>

Stream analytics, comments and recommendations. Await auth state, the page title and anything a crawler reads.

Full code: examples/deferred.md

Pattern 5: useFetcher

A fetcher submits or loads without navigating, which is what inline interactions need.

const fetcher = useFetcher();

// Optimistic UI: the in-flight submission is already in fetcher.formData
const optimisticIsLiked = fetcher.formData
  ? fetcher.formData.get("liked") === "true"
  : isLiked;

<Form> for anything that should change the URL; useFetcher for likes, toggles, inline edits and autocomplete.

Full code: examples/optimistic.md

Pattern 6: Error Boundaries

An exported ErrorBoundary catches everything below it in the route tree, and the first branch tells a thrown Response from an unexpected exception.

export function ErrorBoundary() {
  const error = useRouteError();

  if (isRouteErrorResponse(error)) {
    return (
      <div role="alert">
        <h1>{error.status}</h1>
      </div>
    );
  }

  return (
    <div role="alert">
      <h1>Unexpected Error</h1>
    </div>
  );
}

The boundary is route-scoped, so the rest of the page keeps working.

Full code: examples/error-handling.md

Pattern 7: Meta and Links

meta builds the head tags from the loader's data; links declares stylesheets and preloads.

export const meta: MetaFunction<typeof loader> = ({ data }) => {
  if (!data) return [{ title: "Not Found" }];
  return [
    { title: `${data.post.title} | ${SITE_NAME}` },
    { property: "og:title", content: data.post.title },
    { tagName: "link", rel: "canonical", href: url },
  ];
};

meta receives undefined data when the loader threw, and links cannot see loader data at all — a dynamic <link> goes through meta with tagName: "link".

Full code: examples/meta.md

Pattern 8: Resource Routes

A route module with no default export renders nothing and returns whatever its loader or action does.

// app/routes/api.health.ts
export async function loader() {
  return json({ status: "healthy", timestamp: new Date().toISOString() });
}

Full code: examples/resource-routes.md

Pattern 9: Nested Routes

Nested routes share the parent's layout and load alongside it rather than after it, so an auth check in a parent layout protects every child.

FileRole
admin.tsxLayout — renders <Outlet />
admin._index.tsxWhat renders at /admin itself
admin.users.tsxA child route
admin_.settings.tsx/admin/settings without the admin layout
_auth.tsxA layout that contributes no URL segment

Full code: examples/nested-routes.md

</patterns>

<red_flags>

Red flags

Breaks at runtime:

  • A loader or action exported from a file that is not a route module — nothing calls it
  • <Form> with no method="post" — it submits as a GET, so the action never runs
  • defer() without a <Suspense> and <Await> around the consumer
  • A form action targeting an index route without ?index — the parent's action receives it
  • meta reading data without a null branch — data is undefined when the loader threw
  • A secret in a module a route component imports — route modules are bundled for the browser, unlike loaders and actions

Surprising behaviour:

  • Every parent loader re-runs on a child navigation, so an expensive parent query runs far more often than expected — shouldRevalidate is the control
  • All loaders revalidate after an action, whether or not they relate to what it changed
  • links cannot access loader data, so a dynamic stylesheet URL has to go through meta
  • On React Router v7, clientAction takes priority when both exist, and the server action is skipped unless the clientAction calls it
  • Returning null from a loader compiles and type-checks, and pushes the failure to whatever renders it
  • A useFetcher with no optimistic read of fetcher.formData shows nothing until the round trip finishes

</red_flags>

レビュー

まだレビューはありません。使ってみた感想をお寄せください。

同じリポジトリのスキル

概要と使いどころ

Hugging Face Inference SDK patterns for TypeScript/Node.js — InferenceClient setup, chat completion, text generation, streaming, embeddings, image generation, audio transcription, translation, summarization, and Inference Endpoints

日本語の概要は準備中です。原文の説明を表示しています。

agents-inc/skills242026年9月8日 更新

LiteLLM proxy server setup, TypeScript client patterns via OpenAI SDK, model routing, fallbacks, load balancing, spend tracking, virtual keys, and production deployment

日本語の概要は準備中です。原文の説明を表示しています。

agents-inc/skills242026年9月8日 更新

Serverless GPU compute platform for AI model deployment — web endpoints, GPU functions, model serving, and TypeScript client patterns

日本語の概要は準備中です。原文の説明を表示しています。

agents-inc/skills242026年9月8日 更新

Local LLM inference with the Ollama JavaScript client -- chat, streaming, tool calling, vision, embeddings, structured output, model management, and OpenAI-compatible endpoint

日本語の概要は準備中です。原文の説明を表示しています。

agents-inc/skills242026年9月8日 更新

Replicate SDK patterns for TypeScript/Node.js -- client setup, predictions, streaming, webhooks, file handling, model versioning, deployments, and training

日本語の概要は準備中です。原文の説明を表示しています。

agents-inc/skills242026年9月8日 更新

Together AI SDK patterns for TypeScript — client setup, chat completions, streaming, structured output, function calling, embeddings, image generation, fine-tuning, and OpenAI-compatible endpoints

日本語の概要は準備中です。原文の説明を表示しています。

agents-inc/skills242026年9月8日 更新

agents-inc のスキルをすべて見る

このスキルの問題を報告する