React 18 alpha has been released, which is very exciting! But can we use it with TypeScript?
The answer is “yes,” but we need to do a couple of things to make that happen. This post will show you what to do.
Creating a React app with TypeScript
Let’s create ourselves a vanilla React TypeScript app with Create React App:
yarn create react-app my-app --template typescript
Now let’s upgrade the version of React to @next
:
yarn add [email protected] [email protected]
This will leave you with entries in the package.json
that use React 18. It will likely look something like this:
"react": "^18.0.0-alpha-e6be2d531", "react-dom": "^18.0.0-alpha-e6be2d531",
If we run yarn start
, we’ll find ourselves running a React 18 app. Exciting!
Using the new APIs
So let’s try using the ReactDOM.createRoot
API. It’s this API that opts our application in to using React 18’s new features. We’ll open up index.tsx
and make this change:
-ReactDOM.render(
- <React.StrictMode>
- <App />
- </React.StrictMode>,
- document.getElementById('root')
-);
+const root = ReactDOM.createRoot(document.getElementById('root'));
+
+root.render(
+ <React.StrictMode>
+ <App />
+ </React.StrictMode>
+);
If we were running JavaScript alone, this would work. Because we’re using TypeScript as well, however, we’re now confronted with an error:
Property 'createRoot' does not exist on type 'typeof import("/code/my-app/node_modules/@types/react-dom/index")'. TS2339
This is the TypeScript compiler complaining that it doesn’t know anything about ReactDOM.createRoot
. This is because the type definitions that are currently in place in our application don’t have that API defined.
Let’s upgrade our type definitions:
yarn add @types/react @types/react-dom
We might reasonably hope that everything should work now — alas, it does not. The same error is presenting. TypeScript is not happy.
Telling TypeScript about the new APIs
If we take a look at the PR that added support for the APIs, we’ll find some tips. If you look at one for next.d.ts
, you’ll find this info, courtesy of Sebastian Silbermann:
/** * These are types for things that are present in the upcoming React 18 release. * * Once React 18 is released they can just be moved to the main index file. * * To load the types declared here in an actual project, there are three ways. The easiest one, * if your `tsconfig.json` already has a `"types"` array in the `"compilerOptions"` section, * is to add `"react/next"` to the `"types"` array. * * Alternatively, a specific import syntax can to be used from a typescript file. * This module does not exist in reality, which is why the {} is important: * * ```ts * import {} from 'react/next' * ``` * * It is also possible to include it through a triple-slash reference: * * ```ts * /// <reference types="react/next" /> * ``` * * Either the import or the reference only needs to appear once, anywhere in the project. */
Let’s try the first item on the list. We’ll edit our tsconfig.json
and add a new entry to the "compilerOptions"
section:
"types": ["react/next", "react-dom/next"]
If we restart our build with yarn start
, we’re now presented with a different error:
Argument of type 'HTMLElement | null' is not assignable to parameter of type 'Element | Document | DocumentFragment | Comment'.
Type 'null' is not assignable to type 'Element | Document | DocumentFragment | Comment'. TS2345
Now this actually has nothing to do with the issues with our new React type definitions. They are fine. This is TypeScript saying, “It’s not guaranteed that document.getElementById('root')
returns something that is not null
. Since we’re in strictNullChecks
mode, you need to be sure root
is not null.”
We’ll deal with that by testing whether we do have an element in play before invoking ReactDOM.createRoot
:
-const root = ReactDOM.createRoot(document.getElementById('root'));
+const rootElement = document.getElementById('root');
+if (!rootElement) throw new Error('Failed to find the root element');
+const root = ReactDOM.createRoot(rootElement);
And with that change made, we have a working React 18 application using TypeScript. Enjoy!
LogRocket: Full visibility into your web and mobile apps

LogRocket is a frontend application monitoring solution that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.
In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single-page and mobile apps.
Try it for free.