Skip to content

feat(rsc): allow rejecting server references during decodeReply/decodeAction - #1520

Open
james-elicx wants to merge 2 commits into
vitejs:mainfrom
james-elicx:feat/rsc-on-server-reference
Open

james-elicx wants to merge 2 commits into
vitejs:mainfrom
james-elicx:feat/rsc-on-server-reference

Conversation

@james-elicx

@james-elicx james-elicx commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

Description

decodeReply, decodeAction, and decodeFormState always decode with the plugin's server manifest, which resolves any server reference the client sends. A framework can't limit which server functions a request is allowed to decode, for example to the actions its current route exposes. Next.js does this with a page-scoped server module map, so a server function passed as an argument, or bound to a form action, has to belong to the current page.

Today a framework has to skip these wrappers and call @vitejs/plugin-rsc/vendor/react-server-dom/server.edge directly with its own manifest built on top of createServerManifest().

This adds an experimental onServerReference callback in an optional last parameter, alongside the existing onClientReference extension on renderToReadableStream:

decodeReply(body, { temporaryReferences }, { onServerReference })
decodeAction(formData, { onServerReference })
decodeFormState(result, formData, { onServerReference })

The callback receives { id, name } for each server reference React resolves while decoding. That includes the action itself in decodeAction, and any server function passed as an argument or bound to an action. Throwing rejects the reference before its module loads, and decoding fails. decodeReplyFromBusboy in @vitejs/plugin-rsc/rsc/server.node gets the same parameter. Without the callback, nothing changes.

loadServerAction doesn't call it, so for fetch actions a framework still checks the action id itself. The README says so.

Tests

New e2e/server-reference-callback.test.ts, an inline starter-extra fixture, in dev and build. Its RSC entry rejects one named server reference. Two forms bind a recording action to an allowed and a rejected server function, and each is submitted twice:

  • with JS (decodeReply);
  • without JS (decodeAction).

The rejected submissions fail with a 500 and never run the action. The allowed ones run with the bound server function. Without the source change, the rejected submissions return 200 and run the action.

decodeFormState passes the callback through too, but the test doesn't exercise it. React only decodes there for useActionState forms ($ACTION_KEY), and decodeAction already rejects the same bound references first.

@james-elicx james-elicx changed the title feat(rsc): add onServerReference callback to decode APIs feat(rsc): allow rejecting server references during decodeReply/decodeAction Oct 19, 2026
@james-elicx
james-elicx marked this pull request as ready for review October 19, 2026 10:56

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant