Importing React Through the Ages
by Kent C. Dodds

Before we get started, here's a brief look of valid (today) ways to import React
and use the useState
hook:
// globalwindow.React.useState()// CommonJSconst React = require('react')React.useState()// ESModules default importimport React from 'react'React.useState()// ESModules named importimport {useState} from 'react'useState()// ESModules namespace importimport * as React from 'react'React.useState()
Below I'll explain where each of these mechanisms came from and why I prefer the last one.
Before the dawn of time
I started writing React code back in the React.createClass
days. Here's how we
did things back then:
var React = require('react')var Counter = React.createClass({propTypes: {initialCount: React.PropTypes.number.isRequired,step: React.PropTypes.number,},getDefaultProps: function () {return {step: 1}},getInitialState: function () {var initialCount = this.props.hasOwnProperty('initialCount')? this.props.initialCount: 0return {count: initialCount}},changeCount: function (change) {this.setState(function (previousState) {return {count: previousState.count + change}})}increment: function () {this.changeCount(this.props.step)},decrement: function () {this.changeCount(-this.props.step)},render: function () {return (<div><div>Current Count: {this.state.count}</div><button onClick={this.decrement}>-</button><button onClick={this.increment}>+</button></div>)},})
Yup, var
, React.createClass
, require
, function
. Good times.
Classes and Modules
Eventually, we got ES6 (and later) which included modules, classes, and some other syntax niceties:
import React from 'react'class Counter extends React.Component {state = {count: this.props.initialCount ?? 0}changeCount() {this.setState(({count}) => ({count + change}))}increment = () => this.changeCount(this.props.step)decrement = () => this.changeCount(-this.props.step)render() {return (<div><div>Current Count: {this.state.count}</div><button onClick={this.decrement}>-</button><button onClick={this.increment}>+</button></div>)}}
This is when the "how should I import React" question started popping up. A lot of people preferred doing this:
import React, {Component} from 'react'class Counter extends Component {state = {count: this.props.initialCount ?? 0}changeCount() {this.setState(({count}) => ({count + change}))}increment = () => this.changeCount(this.props.step)decrement = () => this.changeCount(-this.props.step)render() {return (<div><div>Current Count: {this.state.count}</div><button onClick={this.decrement}>-</button><button onClick={this.increment}>+</button></div>)}}
Normally this sort of thing isn't even a question. You just do what the library
exposes. But React never actually exposed ESModules. It exposes itself as either
a global variable called React
or a CommonJS module which is the React
object which has Component
on it (along with other things). But because of the
way the code is compiled, both approaches technically work and neither is
technically "wrong."
One other note... Looking at the code above you might wonder why we can't change that to:
- import React, {Component} from 'react'+ import {Component} from 'react'
The reason you need React
in there is because (at the time) JSX was compiled
to use React:
- <button onClick={this.increment}>+</button>+ React.createElement('button', {onClick: this.increment}, '+')
So if you use JSX, you need to have React
imported as well.
It wasn't long after this that function components became a thing. Even though at the time these couldn't actually be used to manage state or side-effects, they became very popular. I (and many others) got very accustomed to refactoring from class to function and back again (many people just decided it was easier to use class components all the time).
For me, I preferred using function components as much as possible and this is
likely the reason that I preferred using import React from 'react'
and
React.Component
rather than import React, {Component} from 'react'
. I didn't
like having to update my import statement any time I refactored from class
components to function components and vice-versa. Oh, and I know that
IDEs/editors like VSCode and WebStorm have automatic import features, but I
never found those to work very well (I ran into stuff like
this
all the time).
Oh, and one other interesting fact. If you were using TypeScript instead of
Babel, then you'd actually be required to do import * as React from 'react'
unless you enabled
allowSyntheticDefaultImports
.
The Age of Hooks
Then hooks took the scene and the way I wrote components evolved again:
import React from 'react'function Counter({initialCount = 0, step}) {const [count, setCount] = React.useState(initialCount)const decrement = () => setCount((c) => c - step)const increment = () => setCount((c) => c + step)return (<div><div>Current Count: {count}</div><button onClick={decrement}>-</button><button onClick={increment}>+</button></div>)}
This brought up another question on how to import React. We had two ways to use hooks:
import React from 'react'// ...const [count, setCount] = React.useState(initialCount)// alternatively:import React, {useState} from 'react'// ...const [count, setCount] = useState(initialCount)
So again, should we do named imports, or just reference things directly on
React
? Again, for me, I prefer to not have to update my imports every time I
add/remove hooks from my files (and again, I don't trust IDE auto-import), so I
prefer React.useState
over useState
.
The new JSX transform and the future of React + ESM
Finally, React 17 was released with basically no real breaking changes but an announcement of great import (pun intended): There's a new JSX transform that means you no longer need to import React to transform JSX. So now you can write:
function App() {return <h1>Hello World</h1>}
And this will get compiled to:
// Inserted by a compiler (don't import it yourself!)import {jsx as _jsx} from 'react/jsx-runtime'function App() {return _jsx('h1', {children: 'Hello world'})}
So the import happens automatically now! That's great, but that also means that
if you want to migrate to use this new capability, you'll need to remove any
import React from 'react'
that's just for JSX. Luckily the React team made a
script to do this automatically, but they had to make a decision on how to
handle situations where you're using hooks. You have two options:
import * as React from 'react'const [count, setCount] = React.useState(initialCount)// orimport {useState} from 'react'const [count, setCount] = useState(initialCount)
Both of these work today, are technically correct, and will continue to work into the future when React finally exposes an official ESModules build.
The React team decided to go with the named imports approach. I disagree with
this decision for reasons mentioned above (having to update my import all the
time). So for me, I'm now using the import * as React from 'react'
which is a
mouthful for my hands to type, so I have a snippet:
// snippets/javascript.json{"import React": {"prefix": "ir","body": ["import * as React from 'react'\n"]},}
So here's the current state of things:
To clarify:
— danabramov.bsky.social (@dan_abramov) September 23, 2020
✅ import { useState } from 'react'
✅ import * as React from 'react'
🟡 import React from 'react'
The third one is called "default import" and in the long term (maybe in 19 or 20) we will stop supporting it. We provide an automated script to change it.
For me, I'm sticking with import * as React from 'react'
so I don't have to
worry about my imports. And that's my overly long blog post answer for a very
common question I get. Hope it was helpful!