Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Upgrading to react@18 and react-dom@18 fails

I have an existing React 17 app that started life as a create-react-app output with the TypeScript option. I wanted to change package.json to include [email protected] and [email protected] and then start the app, and that step worked fine.

Then I tried to change the index.tsx file the way that the React 18 migration instructions tell you to do it, by changing the relevant segment in index.tsx to:

ReactDOM.createRoot(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById('root')
);

and either npm run build or npm run start gives me compile errors, saying that createRoot is not defined. This is probably due to the fact that I have dependencies on @types/[email protected] and @types/[email protected]. There do not seem to be any updated versions of these files from DefinitelyTyped for React 18 yet.

So, I went back and created a brand new app with npx create-react-app react18 --template typescript. It now wants react@18 and react-dom@18, but still has the old @types/ dependencies. It also gives the same compile error when you switch from render() to createRoot().

As one further step, I asked myself "what happens if we leave out TypeScript?" Created yet another app with npx create-react-app react18js. It depends on [email protected] and [email protected] as expected, but still generates ReactDOM.render() in its index.js output.

That works, but change ReactDOM.render to ReactDOM.createRoot and you get no compile errors, but you also get no visible output if you try npm run start. It doesn't seem to render the <App/> component at all.

Am I missing something? Are the instructions not quite adequate?

like image 395
Craig McClanahan Avatar asked Jan 19 '26 16:01

Craig McClanahan


2 Answers

You need first to import ReactDOM from react-dom/client and then create the root element from the element id=root using createRoot and then use .render to render the content of the <App/> component inside the created root element like this:

import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';

let root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement);

root.render(
  <React.StrictMode>
      <App />
  </React.StrictMode>,
);
like image 86
Yaser AZ Avatar answered Jan 22 '26 05:01

Yaser AZ


The React documentation explains how to use ReactDOM.createRoot, which is not a direct/drop-in replacement for ReactDOM.render.

What the doc doesn't cover is the TypeScript way of using it.

I know this solution is a bit overkill compared to others here that uses type assertion (as HTMLElement) on the DOM element, but it fully utilizes the benefit of strict typing (e.g. if someone accidentally removed the #root element in index.html)

import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App'

const el = document.getElementById('root')
if (el === null) throw new Error('Root container missing in index.html')

const root = ReactDOM.createRoot(el)
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
)
like image 41
Arnel Enero Avatar answered Jan 22 '26 04:01

Arnel Enero



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!