All skills
sergiodxa avatar

/frontend-react-router-best-practices

@9da9532

React Router performance and architecture patterns. Use when writing loaders, actions, forms, routes, or working with React Router data fetching. Triggers on tasks involving React Router routes, data loading, form handling, or route organization.

Use this Skill: https://skilld.dev/gh/sergiodxa/agent-skills/frontend-react-router-best-practices

This session only. Nothing lands on disk.

rulesroute-resource-routes.md

≈952 tokens on demand. Your agent reads this file only when SKILL.md points to it.

Resource Routes

Use resource routes for API-like endpoints without UI.

Why

  • Clean API endpoints within React Router
  • No component rendering overhead
  • Can return any response type (JSON, files, redirects)
  • Reusable across the app with fetcher

What Makes a Resource Route

A resource route has no default export (no component):

import { data } from "react-router";

// routes/api.search.tsx - RESOURCE ROUTE
export async function loader({ request }: Route.LoaderArgs) {
  let url = new URL(request.url);
  let query = url.searchParams.get("q");
  let results = await search(query);
  return data({ results });
}

// No default export = resource route

Common Use Cases

1. Search Endpoints

import { data } from "react-router";

// routes/api.search.tsx
export async function loader({ request }: Route.LoaderArgs) {
  let url = new URL(request.url);
  let query = url.searchParams.get("q") || "";
  let limit = Number(url.searchParams.get("limit")) || 10;

  let results = await search(query, limit);

  return data({ results });
}

Usage with fetcher:

function SearchWidget() {
  let fetcher = useFetcher<typeof searchLoader>();

  return (
    <fetcher.Form action="/api/search" method="get">
      <input name="q" onChange={(e) => fetcher.submit(e.currentTarget.form)} />
      {fetcher.data?.results.map((item) => (
        <SearchItem key={item.id} item={item} />
      ))}
    </fetcher.Form>
  );
}

2. File Downloads

// routes/api.reports.$reportId.download.tsx
export async function loader({ params }: Route.LoaderArgs) {
  let report = await getReport(params.reportId);
  let pdf = await generatePDF(report);

  return new Response(pdf, {
    headers: {
      "Content-Type": "application/pdf",
      "Content-Disposition": `attachment; filename="${report.name}.pdf"`,
    },
  });
}

3. Image Generation

// routes/api.og-image.tsx
export async function loader({ request }: Route.LoaderArgs) {
  let url = new URL(request.url);
  let title = url.searchParams.get("title") || "Default Title";

  let image = await generateOGImage({ title });

  return new Response(image, {
    headers: {
      "Content-Type": "image/png",
      "Cache-Control": "public, max-age=31536000",
    },
  });
}

4. Webhooks

import { data } from "react-router";

// routes/api.webhooks.provider.tsx
export async function action({ request }: Route.ActionArgs) {
  let payload = await request.text();
  let signature = request.headers.get("x-signature");

  let event = verifyWebhook(payload, signature);

  switch (event.type) {
    case "order.completed":
      await handleOrderComplete(event.data);
      break;
    // ... other events
  }

  return data({ received: true });
}

5. Form Actions Without Navigation

import { data } from "react-router";

// routes/api.toggle-favorite.tsx
export async function action({ request }: Route.ActionArgs) {
  let client = await authenticate(request);
  let formData = await request.formData();

  let itemId = z.string().parse(formData.get("itemId"));
  let favorited = formData.get("favorited") === "true";

  let count = await toggleFavorite(client, itemId, favorited);

  return data({ count });
}

Naming Convention

api.search.tsx               -> /api/search
api.webhooks.provider.tsx    -> /api/webhooks/provider
api.reports.$id.download.tsx -> /api/reports/:id/download

Type-Safe Fetcher

import type { loader as searchLoader } from "~/routes/api.search";

function SearchComponent() {
  let fetcher = useFetcher<typeof searchLoader>();

  // fetcher.data is typed from loader return type
  let results = fetcher.data?.results ?? [];
}

Source: SKILL.md on GitHub

2 warnings16d5 checks · Risk SAFE
  • Gen Agent Trust Hub16d

    The skill provides a comprehensive set of developer guidelines and best practices for building React Router applications. It emphasizes performance, type safety, and security measures like input validation (Zod), safe redirects, and CSRF protection (Sec-Fetch). No malicious code, obfuscation, or data exfiltration patterns were detected.

  • Socket16d

    1 alert: gptSecurity

  • Snyk16d

    Risk: LOW · No issues

  • Runlayer7mo

    56/56 files flagged

  • ZeroLeaks5mo

    Score: 93/100 · 2 sections analyzed

Signed by skilld at 9da9532. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 2 months ago.

Dormantupdated 8 months ago

README badge

README badge for sergiodxa/agent-skills/frontend-react-router-best-practices