I’m migrating an app from React 18.3.1 / React-DOM 18.3.1 to React 19.1.0 / React-DOM 19.1.0.
With React 18 the code below rendered an EditView component into every DOM element that SurveyJS inserts with the class .editViewContainer.
After upgrading to React 19 nothing shows up the first time the survey is opened; the containers stay empty. When the survey is reopened later, the component appears but behaves unpredictably.
ComponentCollection.Instance.add({
name: "geometry", // new question type
title: "Geometry", // label in toolbox
questionJSON: {
type: "html",
html: "<div class='editViewContainer'></div>", // placeholder for React
},
});
// 2 – React code that hydrates those placeholders ---------------------------
const rootMap = React.useRef(new Map());
React.useEffect(() => {
// Find (possibly many) <div class="editViewContainer"> nodes SurveyJS just wrote
const containers = Array.from(document.querySelectorAll(".editViewContainer"));
if (showEditView.show) {
// Create a root for each *new* container
containers.forEach((container) => {
if (!rootMap.current.has(container)) {
const root = ReactDOM.createRoot(container);
rootMap.current.set(container, root);
root.render(
<EditView
key={editViewKey}
app={props.app}
model={editModel}
observer={props.localObserver}
surveyJsData={surveyJsData}
resetView={resetEditView}
currentQuestionTitle={currentQuestionTitle}
currentQuestionName={currentQuestionName}
onSaveCallback={handleOnComplete}
ref={editViewRef}
toolbarOptions={showEditView.toolbarOptions}
area={area}
price={price.toFixed(2)}
geofencingWarningToolbar={geofencingWarningToolbar}
drawnGeometryMap={drawnGeometryMap}
geometryValidMap={geometryValidMap}
/>
);
}
});
// Re-render existing roots whose container is still in the DOM
rootMap.current.forEach((root, container) => {
if (containers.includes(container)) {
root.render(
/* …same <EditView> props as above… */
);
}
});
}
}, [
showEditView,
editViewKey,
props.app,
editModel,
props.localObserver,
surveyJsData,
currentQuestionTitle,
currentQuestionName,
handleOnComplete,
editViewRef,
showEditView.toolbarOptions,
area,
price,
geofencingWarningToolbar,
drawnGeometryMap,
geometryValidMap,
]);
Expected behaviour (React 18.3.1)
When the SurveyJS form is opened for the first time, every .editViewContainer immediately shows .
Actual behaviour (React 19.1.0)
On the first open the placeholders stay empty.
After closing and reopening the form (state change that triggers the same useEffect), sometimes appears but its internal state is broken (toolbar buttons disabled, geometry validation not firing, etc.).
What I tried / research so far
Verified that containers is not empty on the first useEffect run (the nodes exist in the DOM).
Replaced ReactDOM.createRoot → ReactDOM.render (deprecated) as a test → still empty in React 19.
Tried moving the logic to useLayoutEffect → no change.
Wrapped the first render in flushSync → no change.
Searched React 19 release notes for breaking changes around external roots (only found the new suspense defaults, which don’t seem related).
Environment
library working failing
react 18.3.1 19.1.0
react-dom 18.3.1 19.1.0
survey-core survey-react-ui”: “^1.9.113
Question
Did React 19 introduce a change that prevents rendering into DOM nodes created outside React (e.g., by SurveyJS) via ReactDOM.createRoot?
If so, what is the correct pattern in React 19 for attaching a component tree to elements that a third-party library inserts after the initial mount?
Any pointers to migration docs, work-arounds, or an explanation of why the first render is skipped would be greatly appreciated.