useControlledHostValueProps always returns a value key, so every field built on it starts uncontrolled and becomes controlled on the first change. react-stately's useControlledState warns on exactly that transition.
Root cause
useControlledHostValueProps.ts:23:
const [value, setValue] = useState(regularValue ?? defaultValue);
// ...
return { ...props, value, onChange };
With neither value nor defaultValue set, value is undefined — react-stately reads that as uncontrolled. The first onChange calls setValue(v), value becomes defined, and the component is now controlled.
The spread is unconditional, so the value key is present even when the caller passed no value at all. That is what makes it systemic rather than per-component.
Blast radius
All eight components using the hook:
NumberField, TextField, TextArea, SearchField, MarkdownEditor, PasswordCreationField, CodeEditor, DateRangePicker
A Storybook sweep saw the warning in 22 stories, among them:
form-controls-numberfield--default, --with-field-error, --with-field-description, --with-contextual-help
form-controls-markdowneditor--default, --auto-resizeable, --show-character-count, --with-on-change, --with-ref
form-controls-passwordcreationfield--with-copy-button, --with-custom-button
form-controls-daterangepicker--with-default-presets, --with-custom-presets
integrations-react-hook-form-numberfield--default, --with-field-error
chat-chat--with-markdown-editor, structure-combine-input-button--with-number-field
The sweep only clicked, it never typed. TextField, TextArea and SearchField warn on keyboard input, so the real story count is higher.
Not a duplicate
This is the third, largest site of the same class.
Direction
useControlledState treats only undefined as uncontrolled, so the fix is to keep each field on one side of that line for its whole lifetime — either omit the value key entirely when the caller is uncontrolled, or commit to controlled from the first render. Which one is right depends on how the remote-value marker path is meant to behave, so it needs a look rather than a mechanical swap.
Note the hook lives in lib/remote/ and exists to protect remote text inputs from interleaved host/remote updates — any fix has to keep that working.
Reproducing
Open iframe.html?id=form-controls-numberfield--default&viewMode=story, click the stepper, and read the console.
Found in a Playwright console sweep over all 443 stories (two full passes, identical results).
useControlledHostValuePropsalways returns avaluekey, so every field built on it starts uncontrolled and becomes controlled on the first change. react-stately'suseControlledStatewarns on exactly that transition.Root cause
useControlledHostValueProps.ts:23:With neither
valuenordefaultValueset,valueisundefined— react-stately reads that as uncontrolled. The firstonChangecallssetValue(v),valuebecomes defined, and the component is now controlled.The spread is unconditional, so the
valuekey is present even when the caller passed no value at all. That is what makes it systemic rather than per-component.Blast radius
All eight components using the hook:
NumberField,TextField,TextArea,SearchField,MarkdownEditor,PasswordCreationField,CodeEditor,DateRangePickerA Storybook sweep saw the warning in 22 stories, among them:
form-controls-numberfield--default,--with-field-error,--with-field-description,--with-contextual-helpform-controls-markdowneditor--default,--auto-resizeable,--show-character-count,--with-on-change,--with-refform-controls-passwordcreationfield--with-copy-button,--with-custom-buttonform-controls-daterangepicker--with-default-presets,--with-custom-presetsintegrations-react-hook-form-numberfield--default,--with-field-errorchat-chat--with-markdown-editor,structure-combine-input-button--with-number-fieldThe sweep only clicked, it never typed.
TextField,TextAreaandSearchFieldwarn on keyboard input, so the real story count is higher.Not a duplicate
TabsandSegmentedControlselection state — a separate code path. None of the stories above are in Two react-aria warnings point at real component bugs: uncontrolled→controlled and PressResponder #3014's list.Modal'sisOpen.This is the third, largest site of the same class.
Direction
useControlledStatetreats onlyundefinedas uncontrolled, so the fix is to keep each field on one side of that line for its whole lifetime — either omit thevaluekey entirely when the caller is uncontrolled, or commit to controlled from the first render. Which one is right depends on how the remote-value marker path is meant to behave, so it needs a look rather than a mechanical swap.Note the hook lives in
lib/remote/and exists to protect remote text inputs from interleaved host/remote updates — any fix has to keep that working.Reproducing
Open
iframe.html?id=form-controls-numberfield--default&viewMode=story, click the stepper, and read the console.Found in a Playwright console sweep over all 443 stories (two full passes, identical results).