Repository navigation
feat(next): let server functions plug into useActionState - #2302
Conversation
Server functions now accept a (prevState, input | FormData) call shape, so they can be passed straight to React's useActionState and keep working before JavaScript loads. Two-argument calls ignore the previous state and deserialize FormData with bracket notation; one-argument calls are unchanged. createServerFunction and createServerFormFunction now share parseFormData for FormData parsing. Co-Authored-By: Claude <noreply@anthropic.com>
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
orpc | bad09e3 | Commit Preview URL Branch Preview URL |
Oct 23 2026, 06:19 AM |
ApprovabilityVerdict: Needs Human Approval Macroscope's review found this PR requires human approval — This adds direct You can add or adjust custom eligibility rules. Learn more. |
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
More templates
@orpc/ai-sdk
@orpc/arktype
@orpc/bun
@orpc/client
@orpc/cloudflare
@orpc/contract
@orpc/experimental-effect
@orpc/evlog
@orpc/hibernation
@orpc/json-schema
@orpc/experimental-lock
@orpc/mcp
@orpc/msw
@orpc/nest
@orpc/next
@orpc/node
@orpc/openapi
@orpc/opentelemetry
@orpc/pinia-colada
@orpc/pino
@orpc/publisher
@orpc/ratelimit
@orpc/server
@orpc/shared
@orpc/swr
@orpc/tanstack-query
@orpc/trpc
@orpc/valibot
@orpc/zod
commit: |
Merging this PR will not alter performance1 warning
Comparing Footnotes
|
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes
Reviewed the full feat(next): let server functions plug into useActionState diff (6 files): the ServerFunction overload and runtime two-argument branch, the createServerFormFunction parser swap, the new runtime/type/jsdom tests, and the Next.js docs section.
ServerFunctiongains theuseActionStatecall shape —createServerFunctionnow branches onargs.length > 1, discardsargs[0]as the previous state, and takesargs[1]as input, deserializingFormDataviaparseFormData(packages/next/src/server-function.ts:82). I verified against the installed React 19.3.1 / Next 16.4.1 bundles that everyuseActionStatepath — client dispatch, programmaticdispatch(), and no-JS progressive enhancement — invokes the action with exactly(prevState, formData | payload), so the heuristic lands on the right argument in all three.- No
$ACTION_*leakage into the input — in the no-JS pathdecodeActionfilters every$ACTION_-prefixed field out before binding the cleanedFormDatato the action, so strict schemas will not see React's bookkeeping keys. createServerFormFunctionnow sharesparseFormData—[...form]→parseFormData(form)is behavior-equivalent (Array.from(form.entries())), as the PR body states.- Tests — runtime coverage for the new shape and single-arg raw-
FormDatapassthrough, a jsdom test driving real ReactuseActionState, and type tests for the overload and inference.pnpm vitest run packages/next,pnpm --filter @orpc/next type:check, the JSDoc backlink check, andblume validate --strictall pass locally.
The two acknowledged behavior changes (the .bind slot reassignment and the added signature narrowing what satisfies ServerFunction) are deliberate, documented in the PR body and the docs warning, and consistent with main being the beta line.
deepseek-v4.1-flash (free via Pullfrog for OSS) | 𝕏
The jsdom useActionState test already proves the previous state is ignored and both FormData and plain input reach the procedure, so the matching unit and type tests are dropped. Its second dispatch is now a valid call, so it fails if the previous state were used as input. Co-Authored-By: Claude <noreply@anthropic.com>
Dismissing prior approval to re-evaluate 07e9221
Co-Authored-By: Claude <noreply@anthropic.com>

Server functions can now be passed straight to React's
useActionState. A two-argument call is treated as(prevState, input | FormData): the previous state is ignored andFormDatais parsed with bracket notation. The state is the usual[error, data]tuple, so forms work, and show validation errors, before JavaScript loads.Behavior changes
FormData.fn.bind(null, input)is now called as(input, formData), so the bound input is treated as the previous state and the form data is validated instead. The docs recommend hidden inputs.ServerFunctionnow has two call signatures, a hand-written async function with a single parameter no longer satisfies theServerFunctiontype (for example, when passed touseServerFunction). Nothing in the repo does this.createServerFormFunctionnow parses withparseFormData, so all three FormData entry points share one parser. Its output is unchanged.Docs
useActionStatesection in the Next.js integration page, with a warning about.bind.Testing
useActionStatewithFormDataand plain input, and fails without this change. A unit test checks that a singleFormDataargument is still passed through unchanged.useActionStateinference:nulland tuple initial states, a mismatched initial state rejected, a procedure with no input. The existing hook type tests still pass.tags[]parsed, and the Zod issue message.