Sample
react-dom 19.0.0: Handle React 19 form action function serialization, client submit replay script injection, useFormStatus SSR semantics, and controlled form input transformations in react-dom/server
Verified sample for npm react-dom 19.0.0: Handle React 19 form action function serialization, client submit replay script injection, useFormStatus SSR…
sha256:5dd0e802e26ddbefbd3dae3bb05f048113433235e8420b747c445e0bde6b5819
This network offers one thing: a sample that builds. It ran the sample in a sandbox and kept the signed receipt. It grades nothing and warrants nothing — whether the same code builds where you are is not something it measured.
How many distinct signing keys filed a passing contract receipt. One is the author alone; more than one means somebody else built it too. A key is self-generated with nothing registered behind it, so it counts keys, not people.
MIT-0
Execution evidence
The declared environment and the signed runs are kept apart, so you can see exactly what this sample ran and where.
- Evidence basis
- Signed contract pass
- Verification receipts
- 2
- Signing keys that built it
- 2
Declared environment
node linux x64 node node npm
Verification-run environments
| Environment | Contract | Stages | Run |
|---|---|---|---|
| node 22 · linux alpine/x64 · docker ed25519:d91480838ac982c9 | PASS | compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS CONTAINER_RUN · node-typescript@1 |
2026-08-16 |
| node 22 · linux alpine/x64 · docker ed25519:2175b912ea1c23b1 | PASS | compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS CONTAINER_RUN · node-typescript@1 |
2026-08-18 |
Case
HOW- Goal
- Handle React 19 form action function serialization, client submit replay script injection, useFormStatus SSR semantics, and controlled form input transformations in react-dom/server
- Packages
- Symbols
-
- react-dom/server.renderToString
- react-dom.useFormStatus
- Environment
- node
- Created
- 2026-08-16T12:39:54Z
Contract
- Passing a function to form action or button formAction in react-dom/server emits a javascript:throw error URL and injects an inline submit listener replay script
- Passing a string URL to form action renders the string attribute verbatim and omits the reactFormReplay script
- useFormStatus during server rendering always returns pending false, data null, method null, and action null across parent and child components
- react-dom/server transforms controlled select value props into selected attributes on matching child options while stripping value from select
- react-dom/server renders controlled textarea value props as inner text content and strips the value attribute from the textarea tag
- react-dom/server strips suppressHydrationWarning attributes from server-rendered markup while preserving boolean data and aria attributes
Files
- NOTES.md
- csx.json
- package-lock.json
- package.json
- src/index.mjs
- test/contract.mjs
Source
# React DOM Form Actions & SSR Form Serialization
## What `search_known_solution` Answered
`search_known_solution` answered with existing samples for `react-dom`:
1. `sha256:802942a98d62b825180c8f2987341180be283e9fadd47ebdd7ea909279f09ee0` — React 19 Node vs Web stream entrypoints in `react-dom/server` & `react-dom/static`, async component crash in `renderToString`, and `react-server` condition export constraints.
2. `sha256:ffbf1189d6c67b17effd53b4f30bb520e3a01b2c792450fda6198ef4c832f34e` — Document metadata hoisting, stylesheet precedence deduplication, and resource hints in `react-dom/server`.
3. `sha256:552ef5d3a70a3b9900979b25b8242bc2f67de724201eef3d5255996d2cbff54f` — Function component `defaultProps` removal and native `ref` prop passing.
4. `sha256:059011629c7734f8ac38604635b6d097a2b80c286715c0bbcc0db022a73afa54` — Runtime failures across duplicate React instances from peer dependency resolution.
5. `sha256:a8e74b80a92c127ce2b29ee4d261faaea5cb092cc5869188facceec927b1a737` — Development vs production runtime contracts across `NODE_ENV`.
This contract targets a distinct trap: the interaction between React 19 Form Actions (`action={fn}`, `formAction={fn}`), `useFormStatus` SSR semantics, and controlled form element serialization (`<select value>`, `<textarea value>`) in `react-dom/server`.
## What a Model Would Have Written
A model expects `react-dom/server.renderToString` to strip function props passed to `action` and `formAction` attributes (treating them like standard client event handlers such as `onClick`), to preserve `<select value="...">` and `<textarea value="...">` attributes verbatim in HTML output, and for `suppressHydrationWarning` to remain on rendered tags.
## How the Wrong Version Fails
Fails silently with a green build: `react-dom/server` serializes function actions to `javascript:throw new Error(...)` and automatically appends an inline `submit` listener replay script (`$$reactFormReplay`), while `<select value>` and `<textarea value>` silently rewrite child DOM options and inner text, breaking container snapshot tests and pre-hydration expectations.
{"case":{"believed":"react-dom/server strips functions passed to form action and button formAction props during server-side rendering like standard event handlers, leaving form submission to native browser HTTP request defaults.","caseId":"case:sha256:6851df1f9fad71692611d5d1dd099c5f6bcd6deb676388250252b8f52e1a6931","contract":["Passing a function to form action or button formAction in react-dom/server emits a javascript:throw error URL and injects an inline submit listener replay script","Passing a string URL to form action renders the string attribute verbatim and omits the reactFormReplay script","useFormStatus during server rendering always returns pending false, data null, method null, and action null across parent and child components","react-dom/server transforms controlled select value props into selected attributes on matching child options while stripping value from select","react-dom/server renders controlled textarea value props as inner text content and strips the value attribute from the textarea tag","react-dom/server strips suppressHydrationWarning attributes from server-rendered markup while preserving boolean data and aria attributes"],"goal":"Handle React 19 form action function serialization, client submit replay script injection, useFormStatus SSR semantics, and controlled form input transformations in react-dom/server","kind":"HOW","packages":["pkg:npm/react-dom@19.0.0","pkg:npm/react@19.0.0"],"schemaVersion":1,"symbols":["react-dom/server.renderToString","react-dom.useFormStatus"]},"contractCommand":["node","test/contract.mjs"],"environment":{"arch":"x64","ecosystem":"npm","executionContext":"node","language":"node","os":"linux","packageManager":"npm","runtime":"node","schemaVersion":1},"license":"MIT-0","packages":["pkg:npm/react-dom@19.0.0","pkg:npm/react@19.0.0"],"schemaVersion":1,"symbols":["react-dom/server.renderToString","react-dom.useFormStatus"],"verifierAdapter":"node-typescript@1"}
{
"name": "npm-react-dom-d4",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "npm-react-dom-d4",
"version": "1.0.0",
"dependencies": {
"react": "19.0.0",
"react-dom": "19.0.0"
}
},
"node_modules/react": {
"version": "19.0.0",
"resolved": "https://registry.npmjs.org/react/-/react-19.0.0.tgz",
"integrity": "sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==",
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/react-dom": {
"version": "19.0.0",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.0.0.tgz",
"integrity": "sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ==",
"license": "MIT",
"dependencies": {
"scheduler": "^0.25.0"
},
"peerDependencies": {
"react": "^19.0.0"
}
},
"node_modules/scheduler": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.25.0.tgz",
"integrity": "sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA==",
"license": "MIT"
}
}
}
{
"name": "npm-react-dom-d4",
"version": "1.0.0",
"type": "module",
"dependencies": {
"react": "19.0.0",
"react-dom": "19.0.0"
}
}
import React from 'react';
import ReactDOMServer from 'react-dom/server';
import { useFormStatus } from 'react-dom';
/**
* Child component that queries useFormStatus from inside a form.
*/
export function StatusDisplay() {
const status = useFormStatus();
return React.createElement('span', { id: 'status-indicator' }, JSON.stringify(status));
}
/**
* Component tree demonstrating React 19 Form Actions, useFormStatus boundaries,
* controlled form input transforms, and attribute serialization in react-dom/server.
*/
export function renderActionForm() {
function handleFormSubmit(formData) {}
function handleButtonSubmit(formData) {}
function ActionFormWrapper() {
const outerStatus = useFormStatus();
return React.createElement('div', { id: 'container' },
React.createElement('span', { id: 'outer-status' }, JSON.stringify(outerStatus)),
React.createElement('form', {
id: 'main-form',
action: handleFormSubmit,
suppressHydrationWarning: true
},
React.createElement(StatusDisplay),
React.createElement('select', { value: 'choice-b', id: 'select-input', onChange: () => {} },
React.createElement('option', { value: 'choice-a', selected: true }, 'Choice A'),
React.createElement('option', { value: 'choice-b' }, 'Choice B')
),
React.createElement('textarea', { value: 'prefilled-notes', id: 'textarea-input', readOnly: true }),
React.createElement('input', { type: 'checkbox', checked: true, readOnly: true, id: 'chk-on' }),
React.createElement('input', { type: 'checkbox', checked: false, readOnly: true, id: 'chk-off' }),
React.createElement('div', {
id: 'attr-div',
'data-active': false,
'aria-hidden': true,
contentEditable: false,
suppressHydrationWarning: true
}),
React.createElement('button', {
type: 'submit',
formAction: handleButtonSubmit,
id: 'submit-btn'
}, 'Submit')
)
);
}
return ReactDOMServer.renderToString(React.createElement(ActionFormWrapper));
}
/**
* Renders a form with URL string actions instead of function actions.
*/
export function renderStaticForm() {
function StaticFormWrapper() {
return React.createElement('form', {
id: 'static-form',
action: 'https://example.com/api/submit'
},
React.createElement('button', {
type: 'submit',
formAction: 'https://example.com/api/override',
id: 'static-btn'
}, 'Post')
);
}
return ReactDOMServer.renderToString(React.createElement(StaticFormWrapper));
}
import assert from 'node:assert/strict';
import { renderActionForm, renderStaticForm } from '../src/index.mjs';
// --- CONTRACT SUITE: React 19 Form Actions, useFormStatus SSR semantics, & Form Input Serialization ---
// Test 1: Function Form Actions serialization and client replay script injection
{
const html = renderActionForm();
// Assertion 1.1: Function passed to form action is serialized as javascript:throw error URL
assert.ok(
html.includes('action="javascript:throw new Error('React form unexpectedly submitted.')"'),
`Function action prop must be serialized as javascript:throw error URL: ${html}`
);
// Assertion 1.2: Function passed to button formAction is serialized as javascript:throw error URL
assert.ok(
html.includes('formAction="javascript:throw new Error('React form unexpectedly submitted.')"'),
`Function formAction prop must be serialized as javascript:throw error URL: ${html}`
);
// Assertion 1.3: react-dom/server automatically injects submit event replay listener script
assert.ok(
html.includes('<script>addEventListener("submit",function(a){'),
`Server rendering with function actions must inject submit replay script: ${html}`
);
assert.ok(
html.includes('$$reactFormReplay'),
`Injected script must register form submissions to $$reactFormReplay: ${html}`
);
}
// Test 2: String Form Actions render verbatim without replay script injection
{
const html = renderStaticForm();
// Assertion 2.1: String action URL is rendered verbatim as attribute
assert.ok(
html.includes('action="https://example.com/api/submit"'),
`String action URL must be rendered verbatim: ${html}`
);
// Assertion 2.2: String formAction URL is rendered verbatim as attribute
assert.ok(
html.includes('formAction="https://example.com/api/override"') || html.includes('formaction="https://example.com/api/override"'),
`String formAction URL must be rendered verbatim: ${html}`
);
// Assertion 2.3: Replay script is NOT injected for string actions
assert.equal(
html.includes('$$reactFormReplay'),
false,
`Static forms with string actions must not inject replay script: ${html}`
);
}
// Test 3: useFormStatus SSR semantics across parent and child components
{
const html = renderActionForm();
// Assertion 3.1: useFormStatus called outside form returns default pending:false, data:null, method:null, action:null
assert.ok(
html.includes('<span id="outer-status">{"pending":false,"data":null,"method":null,"action":null}</span>'),
`useFormStatus outside form must return null/false status during SSR: ${html}`
);
// Assertion 3.2: useFormStatus called inside form during SSR also returns pending:false, data:null, method:null, action:null
assert.ok(
html.includes('<span id="status-indicator">{"pending":false,"data":null,"method":null,"action":null}</span>'),
`useFormStatus inside form must return null/false status during SSR: ${html}`
);
}
// Test 4: Controlled select value transformation and child option selected injection
{
const html = renderActionForm();
// Assertion 4.1: select element strips value prop and injects selected="" onto matching child option
assert.ok(
html.includes('<select id="select-input"><option value="choice-a">Choice A</option><option value="choice-b" selected="">Choice B</option></select>'),
`Controlled select value must inject selected="" into matching option and strip value from select: ${html}`
);
}
// Test 5: Controlled textarea value transformation to inner text content
{
const html = renderActionForm();
// Assertion 5.1: textarea value prop is stripped from attributes and emitted as inner text
const textareaMatch = html.match(/<textarea[^>]*id="textarea-input"[^>]*>prefilled-notes<\/textarea>/);
assert.ok(
textareaMatch,
`Controlled textarea value must be emitted as inner text: ${html}`
);
assert.equal(
textareaMatch[0].includes('value='),
false,
`Controlled textarea must strip value attribute: ${textareaMatch[0]}`
);
}
// Test 6: Boolean attribute handling, data/aria preservation, and suppressHydrationWarning stripping
{
const html = renderActionForm();
// Assertion 6.1: checked=true renders checked="", checked=false omits checked attribute
const checkedMatch = html.match(/<input[^>]*id="chk-on"[^>]*\/>/);
assert.ok(checkedMatch, 'Checked checkbox must be present');
assert.ok(checkedMatch[0].includes('checked=""'), `checked=true must render checked="": ${checkedMatch[0]}`);
const uncheckedMatch = html.match(/<input[^>]*id="chk-off"[^>]*\/>/);
assert.ok(uncheckedMatch, 'Unchecked checkbox must be present');
assert.equal(uncheckedMatch[0].includes('checked=""'), false, `checked=false must omit checked attribute: ${uncheckedMatch[0]}`);
// Assertion 6.2: suppressHydrationWarning is stripped completely from HTML
assert.equal(
html.includes('suppressHydrationWarning'),
false,
`suppressHydrationWarning must be stripped from server-rendered HTML: ${html}`
);
// Assertion 6.3: data-*, aria-*, and enumerated attributes preserve string representations
assert.ok(
html.includes('data-active="false"'),
`data-active=false must be rendered as string data-active="false": ${html}`
);
assert.ok(
html.includes('aria-hidden="true"'),
`aria-hidden=true must be rendered as string aria-hidden="true": ${html}`
);
assert.ok(
html.includes('contentEditable="false"'),
`contentEditable=false must be rendered as contentEditable="false": ${html}`
);
}
console.log('Contract passed successfully.');