본문으로 건너뛰기
cccskills
무료GitHub에 공개

remix-v2-forms-review

Reviews Remix v2 form code for manual fetch() mutations, native <form> misuse, wrong useNavigation/useFetcher choice, missing pending state, unbounded uploads, and intent-pattern violations. Use when reviewing form/mutation code in a Remix v2 codebase.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

설치 방법 보기

포함된 파일(5)

  • SKILL.md9.1 KB
  • references/form-vs-fetcher.md6.2 KB
  • references/multi-action-routes.md5.9 KB
  • references/pending-state.md6.1 KB
  • references/uploads-validation.md6.7 KB

SKILL.md(원문)

설치하기 전에 에이전트에게 전달되는 지시 내용을 확인할 수 있습니다.

Remix v2 Forms Code Review

See remix-v2-forms for canonical patterns. This skill flags violations; the sibling skill teaches the patterns.

Quick Reference

Issue TypeReference
Manual fetch(), native <form>, wrong <Form> vs useFetcher choicereferences/form-vs-fetcher.md
useState loading flags, useNavigation for per-row, missing pending statereferences/pending-state.md
Unbounded memory uploads, missing encType, unvalidated FormData, mirrored optimistic statereferences/uploads-validation.md
Route sprawl instead of intent pattern, PUT/DELETE without PE fallbackreferences/multi-action-routes.md

Review Checklist

  • In-app mutations use <Form> or <fetcher.Form>, never fetch() / axios
  • <Form> is imported from @remix-run/react (not native <form>) for POST mutations
  • useFetcher used when URL should NOT change (row toggle, inline edit)
  • <Form> + redirect(...) used when URL SHOULD change (create, delete-then-list)
  • Pending state derived from useNavigation() or fetcher.state, never useState
  • Per-row pending uses per-row fetcher.state (not page-global useNavigation)
  • useNavigation() calls check navigation.formAction to scope to expected path
  • Optimistic UI reads fetcher.formData / navigation.formData directly (not mirrored)
  • Actions returning success redirect(...), returning json() only for errors / same-page
  • <Form encType="multipart/form-data"> on every file-upload form
  • unstable_createMemoryUploadHandler always has maxPartSize; large files use disk/stream handler
  • FormData values are validated/coerced before reaching the DB (no form.get(x) as string)
  • Multiple mutations on one route use intent pattern, not separate routes
  • method="put|patch|delete" is documented as JS-only, or rewritten as POST + intent
  • nav.formMethod / fetcher.formMethod compared against UPPERCASE strings ("POST", "GET"); v2's v2_normalizeFormMethod default returns UPPERCASE — === "post" silently never matches.

Valid Patterns (Do NOT Flag)

These are correct Remix v2 usage and should not be reported:

  • <Form> without action prop — posts to the current URL by convention; explicit action is optional.
  • GET <Form> — legitimate for search/filter UIs; hits the loader with form fields as URL search params and does NOT call an action. Most "hygiene" rules (intent, redirect, encType) apply only to POST forms.
  • Multiple useFetcher() instances on one page — each call returns an independent submission channel; intentional for parallel mutations to different rows.
  • useSubmit() in an event handler — correct programmatic submission for autosave, keyboard shortcuts, or onChange triggers.
  • Reading fetcher.formData during a submission — intended; this is the canonical optimistic source.
  • useActionData data persisting after submission — known behavior; it returns the last action result until the next navigation or action.
  • navigate={false} on <Form> — turns it into a fetcher form; equivalent to <fetcher.Form> without holding a fetcher ref.
  • unstable_ prefix on parseMultipartFormData / upload handlers — permanent in v2; do not flag as "unstable API".

Context-Sensitive Rules

Only flag these when the listed condition holds:

IssueFlag ONLY IF
Native <form> instead of <Form>Method is POST and the route has an action — GET forms and external-URL forms are fine
Missing pending stateThe form is POST and there is no useNavigation() / fetcher.state read anywhere in the component
Action returns json({ ok: true }) after a createThe route is a "/new" or creation surface — same-page edit forms legitimately return JSON
method="put" / "patch" / "delete"Progressive enhancement is in scope for the surface (public app) — admin/JS-only tools may opt out if documented
Unbounded unstable_createMemoryUploadHandlerThe upload accepts user-controlled files (not a fixed-size internal artifact)
Separate routes per mutationThe mutations operate on the same resource with compatible auth — sibling resources with different rules are fine
useNavigation() without formAction filterThe component contains other navigation surfaces (sidebar <Link>, sibling forms) that would trigger false positives
Mirroring fetcher.formData into stateThe shadowed value drives a user-visible element (button label, count, toggle) — a local "is-editing" flag is unrelated

Hard gates (before writing findings)

Run these in order. Do not draft user-facing findings until every gate passes for the batch you are about to report.

  1. Location evidence — Pass: Each issue lists a repo path and either a line range or a short verbatim quote from the file you read (not from memory or diff-only guesswork). Name the route module, the component, and the action if one exists.

  2. Exemption check — Pass: For each issue, you can state in one line why it is not covered by Valid Patterns (Do NOT Flag) and any matching row in Context-Sensitive Rules.

  3. Form-method check — Pass: Before flagging missing intent, missing encType, missing redirect, or missing pending state, you have confirmed the form is method="post" (or put|patch|delete). GET forms are legitimate for search/filter and trigger loaders, not actions — applying POST-form rules to them is a false positive.

  4. Protocol — Pass: You completed the Pre-Report Verification Checklist in review-verification-protocol for this review.

Additional Documentation

When to Load References

  • Reviewing forms that call fetch() / axios / native <form>, or choose between <Form> and useFetcher → form-vs-fetcher.md
  • Reviewing loading flags, spinners, disabled-button logic, per-row pending → pending-state.md
  • Reviewing file uploads, unstable_* handlers, FormData parsing, optimistic UI → uploads-validation.md
  • Reviewing routes with multiple mutations, intent fields, PUT/DELETE methods → multi-action-routes.md

Review Questions

  1. Does every in-app mutation flow through a route action (no manual fetch())?
  2. Is the <Form> vs useFetcher choice driven by whether the URL should change?
  3. Is pending state derived from useNavigation() / fetcher.state (never useState)?
  4. Are per-row spinners wired to per-row fetcher.state (not page-global useNavigation)?
  5. Do file-upload forms set encType="multipart/form-data" and use bounded handlers?
  6. Are FormData values validated before reaching the DB?
  7. Do multiple mutations on one resource use the intent pattern, not separate routes?
  8. Do POST forms have at least one real submit button for progressive enhancement?

False-Positive Notes

  • A <Form> rendering inside a non-route component is still tied to the nearest route's action — read the route file before flagging "missing action".
  • useActionData() returning data after a successful submission is expected behavior; the data persists until the next navigation. Only flag if a success banner is rendered unconditionally without a dismiss path.
  • Code that imports Form aliased (e.g. import { Form as RemixForm }) is still the Remix component — match on import source, not local name.

Before Submitting Findings

Complete Hard gates (especially gate 4), then report only issues that still pass the review-verification-protocol pre-report checks.

리뷰

아직 리뷰가 없습니다. 사용해 본 소감을 남겨 주세요.

댓글

로그인 없이 쓸 수 있습니다. 사용 후기, 질문, 작동하지 않은 환경 등을 편하게 남겨 주세요.

아직 댓글이 없습니다. 첫 댓글을 남겨 보세요.

보내면 바로 공개됩니다. 이메일 주소나 전화번호 같은 개인 정보는 쓰지 마세요. URL은 2개까지입니다.

0 / 1,000자

이 양식은 reCAPTCHA로 보호되며 Google 개인정보처리방침 및 서비스 약관이 적용됩니다.

같은 저장소의 스킬

Use when you need to mine a conversation, session transcript, or design discussion for architectural decisions before writing ADRs. Identifies problem-solution pairs, trade-off debates, technology choices, and explicit "[ADR]" tags. Triggers on "what decisions did we make", "extract decisions from this chat", "find the choices in our discussion", or "summarize architectural decisions". Also useful after long planning sessions to capture decisions that were made implicitly. Does NOT write ADR documents — use adr-writing or write-adr for that.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

existential-birds/beagle842026년 9월 16일 업데이트

Use when writing or formatting an ADR document using the MADR template, applying Definition of Done (E.C.A.D.R.) criteria, or verifying ADR completeness. Triggers on "write the ADR", "format as MADR", "check ADR quality", "mark gaps in ADR". Also triggers when a decision has been extracted and needs to become a document. Does NOT extract decisions from conversations (use adr-decision-extraction) or orchestrate the full extract-confirm-write workflow (use write-adr).

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

existential-birds/beagle842026년 9월 16일 업데이트

Use when auditing an agent codebase against the 12-Factor Agents methodology, reviewing LLM-powered system architecture, or assessing agentic app compliance. Triggers on "analyze agent architecture", "12-factor audit", "how compliant is this agent", or "evaluate this LLM app". Also applies when comparing frameworks or planning agent improvements. Not for quick checklists — this performs deep per-factor codebase analysis with file-level evidence.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

existential-birds/beagle842026년 9월 16일 업데이트

Vercel AI Elements for workflow UI components. Use when building chat interfaces, displaying tool execution, showing reasoning/thinking, or creating job queues. Triggers on ai-elements, Queue, Confirmation, Tool, Reasoning, Shimmer, Loader, Message, Conversation, PromptInput.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

existential-birds/beagle842026년 9월 16일 업데이트

Reviews App Intents code for intent structure, entities, shortcuts, and parameters. Use when reviewing code with import AppIntents, @AppIntent, AppEntity, AppShortcutsProvider, or @Parameter.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

existential-birds/beagle842026년 9월 16일 업데이트

Use when the user wants a cited, structured read of local documents and project knowledge. Triggers on: "analyze these docs", "scan my project for context", "read the docs folder", "summarize what's in .beagle/concepts/", "extract context from docs/", "what's in this folder", "go read everything in X and tell me what's there". Also invoked programmatically by other beagle skills (prfaq-beagle Ignition, brainstorm-beagle reference points, strategy-interview context grounding) via the companion contract. Does NOT trigger on codebase lookups ("find this function", "search the repo"), web research (use web-research), LLM-as-judge evaluation (use llm-judge), or document editing (use humanize-beagle). Produces a written scan plan, parallel-subagent findings, and a cited synthesis report on disk — never inline prose, never unsourced claims.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

existential-birds/beagle842026년 9월 16일 업데이트

existential-birds의 스킬 모두 보기

이 스킬의 문제 신고