Skip to content

React.js 17: No New Features and New JSX Transform

React.js 17: No New Features and New JSX Transform

React.js 17: No New Features and New JSX Transform

When React.js 17 became available on October 20th, it generated some confusion because it has no new developer-facing features.

Instead, the React Core team focused on making the React library easier to self-upgrade in the future. This means, that if your app is running React 17 and you need to upgrade some features to React 18, you can move your application core to React 18 and lazily load the legacy code without any issues.

Of course, you can still upgrade your entire app the new version at once, which is the recommended version by the React team.

Installation

To install React 17 with npm, run:

npm install react@17.0.0 react-dom@17.0.0

To install React 17 with Yarn, run:

yarn add react@17.0.0 react-dom@17.0.0

Internal improvements

New JSX Transform

One of the greatest internal improvements React 17 has is the new JSX transform. Prior to React 17, when you write JSX code, it was transformed to a React.createElement call.

So, it looked something like this:

import React from 'react'

const App = () => {
  return <h1>Hello World</h1>
}

export default App

Was transformed to this:

import React from 'react'

const App = () => {
  return React.createElement('h1', null, 'Hello World')
}

export default App

Starting on React 17, you don't need to import React from 'react' anymore. Instead, two new entry points were added to the React package that are intended to be used only by compilers like Babel or TypeScript. So for the above JSX code, the output will be slightly different:

import { jsx as _jsx } from 'react/jsx-runtime'

const App = () => {
  return _jsx('h1', { children: 'Hello world' })
}

export default App

So, your app won't need to import React from 'react' anymore. Instead, you can directly write your JSX code:

const App = () => {
  return <h1>Hello World</h1>
}

export default App

Next.js was known to allow the above because under the hood, it imported React. However, they changed this in their 9.5.3 release in order to use the new transform.

Updating ESlint rules

If you are using the eslint-plugin-react package, you may notice not importing React will generate an issue. For that, you can just disable those rules while they remove those rules.

eslintrc.json

{
  // ...
  "rules": {
    // ...
    "react/jsx-uses-react": "off",
    "react/react-in-jsx-scope": "off"
  }
}

Removing Unused React Imports

Luckily, the React team has created a codemod you can run to remove your old React imports. To run it, just open a new terminal on your project directory, and run the following script:

npx react-codemod update-react-imports

A new Event Delegation System

To enable gradual updates, the React team changed its internal Event Delegation System, which works like this:

Before React 17, all event handlers were attached at the document level under the hood, so it ran document.addEventListener() for most events.

After React 17, it attached all event handlers to the root element where you render your app, which calls rootNode.addEventListener() instead.

To better understand how the new Event Delegation System works, take a look at this diagram:

New React Event Delegation System

You can take a look at a demo of gradual updates on an example repository put together by the React team.

Other Breaking Changes

There are other minor breaking changes that hopefully won't affect your application. They are mostly internal changes, like the onScroll event no longer bubbling, onFocus and onBlur events switching to use the native focusin and focusout under the hood, removing the old event pooling optimization, and making the effect cleanup timing more consistent.

To go deeper into these other breaking changes, you can take a look at the React v17 RC Breaking Changes.