createRoot is the whole React 18 opt-in

Upgrading the package changes almost nothing; replacing render with createRoot is what switches the application into concurrent mode.

// 17, and still supported in 18 with a console warning
import ReactDOM from 'react-dom'
ReactDOM.render(<App />, document.getElementById('root'))

// 18
import { createRoot } from 'react-dom/client'
createRoot(document.getElementById('root')).render(<App />)

// note the import path: react-dom/client, not react-dom.
// the old entry point still exists and opts you out of
// every concurrent feature, silently.

Leaving the old call in place is a supported and entirely reasonable first step, and it means useTransition and automatic batching do nothing — which is confusing when somebody adds them and measures no difference. The console warning is the only signal, and it is one line among whatever else is in the console.