React 17: New JSX Transform

A guide to integrating the new JSX Transform

By Michael Chang

December 16th, 2020

image

My Journey

Every major release of any dependency leads to thoughts of “Oh man, I hope this major upgrade isn’t too painful”. When I saw that React had been released on npm outdated, my reaction was no different. But after looking at their React 17 release blog post, I breathed a sign of relief — there were no breaking changes or new features. But, it did mention an interesting optional change: the new JSX Transform.

JSX Transform

JSX transform is needed so your source code can be converted into code the browser understands. This is where compilers such as Babel or TypeScript come in — they convert your developer-friendly code into browser-friendly code. The old JSX Transform required you to import React since this would tell your compiler that this file would require some React JSX magic. Otherwise, the compilation will fail. This is why the ESLint plugin eslint-plugin-react includes and recommends the react/react-in-jsx-scope and react/jsx-uses-react rules.

The new JSX transform, however, no longer requires the React import. The compilation will still work without a single change to your JSX code! Internally, the transform magic works differently, but the end result is the same — your code works exactly as before in the browser.

React Utils

While you no longer need to import React, you still may have to import the React utils, hooks, and other tools if you use them. These should be imported via their named imports.

import { Component, lazy, Suspense, useState } from 'react';

ESLint

This new transform will conflict with some ESLint rules. If you are using react/react-in-jsx-scope or react/jsx-uses-react, either explicitly or through a common configuration (such as Airbnb’s ESLint configuration or eslint-plugin-react’s recommended), they need to be disabled.

// .eslintrc.js
module.exports = {
  rules: {
    'react/react-in-jsx-scope': 'off',
    'react/jsx-uses-react': 'off'
  }
}

Configuration

This feature does require a small bit of configuration though. If you’re using Create React App’s react-scripts, you’ll have to unfortunately bite the bullet and upgrade to 4.0.

If you are manually setting up Babel, support for this transform requires at least Babel 7.9 and either @babel/preset-react or @babel/plugin-transform-react-jsx using automatic runtime in your babel configuration file.

// for @babel/preset-react
{
  "presets": [
    ["@babel/preset-react", {
      "runtime": "automatic"
    }]
  ]
}

// for @babel/plugin-transform-react-jsx
{
  "plugins": [
    ["@babel/plugin-transform-react-jsx", {
      "runtime": "automatic"
    }]
  ]
}

Codemod

To support this migration, the React team has thankfully graced us with a codemod:

npx react-codemod update-react-imports

After a few questions about your environment, this script should automatically remove default React imports and add in named imports if you use any React utils. Of course, verify the changes as this codemod may not be perfect.

Note: I personally encountered a MODULE_NOT_FOUND (Error: Cannot find module ‘@babel/runtime-corejs3/helpers/interopRequireDefault’) issue that I still have not found the root cause for. But I did find a workaround by temporarily deleting (or commenting out) the babel configuration file.

Supported Versions

Besides React 17, the React team has also backported this feature to React 16.14, React 15.7, and React 0.14.10 so you can take advantage of this feature without dealing with a major upgrade. But if you’re on React 16, the upgrade to React 17 is painless.

Final Thoughts

Is there any benefit to this? Not really. But do I like this change? Yes absolutely. It always felt silly to me that I would import something that wasn’t (explicitly) used as if it was an unused import or variable. Since the new JSX transform requires minimal configuration changes and has a codemod to automatically refactor everything, you may as well just start using it.

Resources



Continue Learning