selbekk

Diving into the new JSX transform

Diving into the new JSX transform

September 23, 2020
4 min read
Originally published at blog.logrocket.com

React 17 came with a fresh new transform for JSX. Learn what that means for you!

With React 17, you no longer need to import React in your files to use React. Confused? This article will tell you what you need to know in order to migrate both your code and knowledge to this new way of doing things.

What’s a JSX transform?

If you’ve ever written React, you’ve probably noticed the weird HTML-looking syntax in regular JavaScript files. That syntax is called JSX, and is not valid JavaScript. It looks like this:

const Welcome = () => {
  return <h1 className="hero">Welcome!</h1>;
};

In order to run this in a browser, you need to run your code through a compiler, typically Babel or TypeScript, which changes your code to valid JavaScript. Because, as it turns out, JSX is just another way of writing this:

const Welcome = () => {
  return React.createElement("h1", { className: "hero", children: "Welcome" });
};

So Babel or TypeScript (or whatever tool you choose) changes your code into calls to the function React.createElement – and therein lies the challenge. Because, suddenly, you have a reference to “React” in your code! If you haven’t imported React at the top of your file, JavaScript doesn’t know what to do with this compiled code either. That’s why you need to add import React from 'react' to the top of all your JSX-files.

Adding this to the top of all files is a huge stumbling block for beginners and a pain in the butt for the experts. And with React 17, you no longer need to specify it!

The React team has collaborated with the community to create a new transform, which automatically imports a new jsx-function from a new custom entry point. Your compiled code will now look like this:

import { jsx as _jsx } from "react/jsx-runtime";

const Welcome = () => {
  return _jsx("h1", { className: "hero", children: "Welcome" });
};

This will also have a lovely side-effect of a potentially smaller bundle size!

What do I need to do?

First, I want to reiterate that this feature is only available in React 17’s release candidate right now. It will be back-ported to older versions as well, but if you want to try it out today, you need to stick with version 17.

What you need to do depends on what you’re using to write your React app.

  • If you’re using create-react-app, you need to update to version 4.0.0 (currently in beta)
  • If you’re using Next JS, you need to update to version 9.5.3 or above
  • If you’re using Gatsby JS, you need to update to version 2.24.5 or above

If you have your own Babel config, you need to do a few more manual steps. If you’re using @babel/preset-react, update it to the newest version. If you’re using @babel/plugin-transform-react-jsx, update that one instead. And update @babel/core as well, while you’re at it.

npm update @babel/core @babel/preset-react

# or

npm update @babel/core @babel/plugin-transform-react-jsx

Next, update your Babel config with the following snippets:

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

or

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

And you’re done!

Removing existing imports

Well, almost done. Because now you have tons of imports that aren’t necessary anymore! And we can’t let them hang around, can we?

Well, actually, you can. Everything will work as normal and will continue to do so for the foreseeable future. But having dead code hanging around is just a nuisance, and if you have the resources to get them removed, why not?

Instead of going through all your hundreds of components removing this by hand, the great people over at the React team have written an automatic script (a so-called codemod) that does it all for us. 🙌 All you need to do is the following:

npx react-codemod update-react-imports

This will remove all imports to the default React export (import React from 'react'), and rewrite any references to hooks and other functions as named imports instead. That means React.useEffect will change to useEffect instead, and an import { useEffect } from 'react' statement will be added to the top of your file.

This latter part took me by surprise (I’ve always written them with prefix because I find it easier to both read and write), but it clears the way for creating an ES module build of React in the future. I’m sure I’ll get used to it after a while 😅

The payoff

With this annoying React import out of the way, React will be even easier to learn. There’s one less concept to remember, and things work better out of the box. And one line less to write whenever you’re bootstrapping a new JSX file.

All rights reserved © 2024