Introducing the New JSX Transform
Although React 17 doesn’t contain new features, it will provide support for a new version of the JSX transform. In this post, we will describe what it is and how to try it.
What’s a JSX Transform?
Browsers don’t understand JSX out of the box, so most React users rely on a compiler like Babel or TypeScript to transform JSX code into regular JavaScript. Many preconfigured toolkits like Create React App or Next.js also include a JSX transform under the hood.
Together with the React 17 release, we’ve wanted to make a few improvements to the JSX transform, but we didn’t want to break existing setups. This is why we worked with Babel to offer a new, rewritten version of the JSX transform for people who would like to upgrade.
Upgrading to the new transform is completely optional, but it has a few benefits:
- With the new transform, you can use JSX without importing React.
- Depending on your setup, its compiled output may slightly improve the bundle size.
- It will enable future improvements that reduce the number of concepts you need to learn React.
This upgrade will not change the JSX syntax and is not required. The old JSX transform will keep working as usual, and there are no plans to remove the support for it.
React 17 RC already includes support for the new transform, so go give it a try! To make it easier to adopt, we’ve also backported its support to React 16.14.0, React 15.7.0, and React 0.14.10. You can find the upgrade instructions for different tools below.
Now let’s take a closer look at the differences between the old and the new transform.
What’s Different in the New Transform?
When you use JSX, the compiler transforms it into React function calls that the browser can understand. The old JSX transform turned JSX into React.createElement(…) calls.
For example,
let’s say your source code looks like this:
Under the hood, the old JSX transform turns it into regular JavaScript:
However, this is not perfect:
- Because JSX was compiled into React.createElement, React needed to be in scope if you used JSX.
- There are some performance improvements and simplifications that React.createElement does not allow.
To solve these issues, React 17 introduces two new entry points to the React package that are intended to only be used by compilers like Babel and TypeScript. Instead of transforming JSX to React.createElement, the new JSX transform automatically imports special functions from those new entry points in the React package and calls them.
Let’s say that your source code looks like this:
This is what the new JSX transform compiles it to:
Note how our original code did not need to import React to use JSX anymore! (But we would still need to import React in order to use Hooks or other exports that React provides.)
This change is fully compatible with all of the existing JSX code, so you won’t have to change your components. If you’re curious, you can check out the technical RFC for more details about how the new transform works.
How to Upgrade to the New JSX Transform
If you aren’t ready to upgrade to the new JSX transform or if you are using JSX for another library, don’t worry. The old transform will not be removed and will continue to be supported.
If you want to upgrade, you will need two things:
- A version of React that supports the new transform (React 17 RC and higher supports it, but we’ve also released React 16.14.0, React 15.7.0, and React 0.14.10 for people who are still on the older major versions).
- A compatible compiler (see instructions for different tools below).
Since the new JSX transform doesn’t require React to be in scope, we’ve also prepared an automated script that will remove the unnecessary imports from your codebase.
Create React App
Create React App 4.0.0+ uses the new transform for compatible React versions.
Next.js
Next.js v9.5.3+ uses the new transform for compatible React versions.
Gatsby
Gatsby v2.24.5+ uses the new transform for compatible React versions.
Manual Babel Setup
Support for the new JSX transform is available in Babel v7.9.0 and above.
First, you’ll need to update to the latest Babel and plugin transform.
If you are using @babel/plugin-transform-react-jsx:
If you are using @babel/preset-react:
Currently, the old transform {“runtime”: “classic”} is the default option. To enable the new transform, you can pass {“runtime”: “automatic”} as an option to @babel/plugin-transform-react-jsx or @babel/preset-react:
Starting from Babel 8, “automatic” will be the default runtime for both plugins. For more information, check out the Babel documentation for @babel/plugin-transform-react-jsx and @babel/preset-react.
ESLint
If you are using eslint-plugin-react, the react/jsx-uses-react and react/react-in-jsx-scope rules are no longer necessary and can be turned off or removed.
TypeScript
TypeScript supports the new JSX transform in v4.1 and up.
Flow
Flow supports the new JSX transform in v0.126.0 and up, by adding react.runtime=automatic to your Flow configuration options.
Removing Unused React Imports
Because the new JSX transform will automatically import the necessary react/jsx-runtime functions, React will no longer need to be in scope when you use JSX. This might lead to unused React imports in your code. It doesn’t hurt to keep them, but if you’d like to remove them, we recommend running a “codemod” script to remove them automatically:
Running this codemod will:
- Remove all unused React imports as a result of upgrading to the new JSX transform.
- Change all default React imports (i.e. import React from “react”) to destructured named imports (ex. import { useState } from “react”) which is the preferred style going into the future. This codemod will not affect the existing namespace imports (i.e. import * as React from “react”) which is also a valid style. The default imports will keep working in React 17, but in the longer term we encourage moving away from them.
For example,
will be replaced with
In addition to cleaning up unused imports, this will also help you prepare for a future major version of React (not React 17) which will support ES Modules and not have a default export.
For example,
will be replaced with
In addition to cleaning up unused imports, this will also help you prepare for a future major version of React (not React 17) which will support ES Modules and not have a default export.