This warning appears when attempting to use the return value of ReactDOM.render() to access a component instance. React warns against this because it is a legacy pattern that may not work with future async rendering features.
This warning indicates that you are trying to use the return value of ReactDOM.render() to get a reference to the rendered component instance. While ReactDOM.render() historically returned a reference to the root component (for class components), this behavior is being deprecated because it is incompatible with React's concurrent rendering features. In React 17 and earlier, ReactDOM.render() could return a component instance for class components, allowing direct access to the component's methods and state. However, this pattern was never recommended and becomes problematic with React 18's concurrent rendering, where components may render asynchronously. This means the component might not be fully mounted when ReactDOM.render() returns, creating race conditions. The React team has deprecated this pattern in favor of using refs, which provide a more reliable and future-proof way to access component instances. In React 18 and later, you should migrate to the createRoot API, which does not return a component reference at all.
The recommended approach is to use a callback ref attached to your root component:
// Before (legacy pattern)
const componentInstance = ReactDOM.render(
<MyComponent />,
document.getElementById('root')
);
componentInstance.someMethod();
// After (recommended pattern)
let componentInstance;
ReactDOM.render(
<MyComponent ref={(ref) => { componentInstance = ref; }} />,
document.getElementById('root')
);
// Use componentInstance after it's set
if (componentInstance) {
componentInstance.someMethod();
}The callback ref will be called with the component instance once it's mounted, providing safe access to the component.
If you're using React 18 or later, migrate to the new createRoot API which doesn't return a component reference:
// Legacy React 17
import ReactDOM from 'react-dom';
ReactDOM.render(<App />, document.getElementById('root'));
// React 18+
import { createRoot } from 'react-dom/client';
const root = createRoot(document.getElementById('root'));
root.render(<App />);With createRoot, you should use refs within your component tree rather than accessing the root component externally.
Consider whether you actually need direct access to the component instance. Often, this pattern can be replaced with better React practices:
// Instead of calling methods on component instance
// Use state lifting and props
// Before
const app = ReactDOM.render(<App />, root);
app.updateData(newData);
// After - lift state up or use context
function Parent() {
const [data, setData] = useState(initialData);
return <App data={data} onUpdate={setData} />;
}
root.render(<Parent />);This approach is more idiomatic and works better with React's unidirectional data flow.
If you need to trigger actions or access data across your component tree, use React Context instead of component references:
const AppContext = React.createContext();
function App() {
const [state, setState] = useState(initialState);
const api = {
updateData: (data) => setState(data),
// other methods
};
return (
<AppContext.Provider value={api}>
<YourComponents />
</AppContext.Provider>
);
}
// In any child component
function ChildComponent() {
const { updateData } = useContext(AppContext);
return <button onClick={() => updateData(newData)}>Update</button>;
}Why This Pattern Was Deprecated:
The return value of ReactDOM.render() creates tight coupling between the rendering logic and component internals, which goes against React's declarative programming model. With React 18's concurrent features, rendering can be interruptible and may not complete synchronously, making the return value unreliable.
React 19 Changes:
In React 19, the need for forwardRef is being reduced as refs can be passed as normal props. This simplifies ref handling throughout your application. However, the createRoot API remains the recommended approach for mounting React applications.
Integration Testing:
If you need component references for testing purposes, use testing libraries like React Testing Library which provide better APIs for interacting with rendered components without relying on internal references:
import { render, screen } from '@testing-library/react';
const { container } = render(<MyComponent />);
const element = screen.getByRole('button');Legacy Code Migration:
When migrating large codebases, you can gradually refactor code that uses render() return values. Start by identifying all instances where the return value is used (ESLint's react/no-render-return-value rule helps with this) and prioritize based on how frequently each component is accessed.
React.FC expects children prop to be defined
React.FC no longer includes implicit children prop
Warning: You provided a `selected` prop to a form field without an `onChange` handler
You provided a 'selected' prop without an onChange handler
Failed to load source map from suspense chunk
How to fix "Failed to load source map from suspense chunk" in React
Prop spreading could cause security issues
Prop spreading could cause security issues
React Hook useCallback has a missing dependency: 'variable'. Either include it or remove the dependency array react-hooks/exhaustive-deps
React Hook useCallback has a missing dependency