JSX without importing React
Tags: javascript, react, webdev, programming
Published: 2022-02-03 19:48:50

React 17 provides support for a new version of the JSX transform.
Simply put, it allows using JSX without having React in scope.

Let's figure out why it's possible and how it works.

## The previous state of things ⏮️

Prior to React v17.0.0, JSX transform used React.createElement internally.
There was one minor and one major problem with this approach:
👉 React must be in scope
👉 "Some performance improvements and simplifications" weren't possible

```jsx
// Before transpilation
import React from 'react';

const Item = () => {
  return <div>Hello world!</div>;
}

// After transpilation
// React is available in global scope

const Item = () => {
  return React.createElement('div', null, 'Hello world!');
}
```

## A whole new world ✨

Starting from React 17, JSX transform use special jsx function internally.
You don't need to import it. Instead, the transpiler automatically imports it from React package at build time.

```js
// Before transpilation
const Item = () => {
  return <div>Hello world!</div>;
}

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

const Item = () => {
  return _jsx('div', {children: 'Hello world!'});
}
```

## createElement 🆚 jsx


![createElement vs jsx](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k8nkpo9mwhefcaf2y6vu.png)

These two functions serve the same purpose, but they are different in a few ways.
Let's take a thorough look at them and examine how it all works under the hood.

### API difference

createElement takes three arguments:
👉 element type (tag name, function/class, React.Fragment)
👉 props, passed to the element
👉 children of the element

Only the first argument is mandatory.

```js
/**
* @param type - type of the element
* @param config - props passed to the element
* @param children - children of the element
*/
function createElement(type, config, children) {
  // ...
}
```

### jsx takes three arguments too, but they aren't the same.

👉 element type is exactly the same
👉 props, including children and excluding key
👉 key, that you use to create lists of elements

Here only the first argument is mandatory too.

```js
/**
* @param type - type of the element
* @param config - props passed to the element, including children and excluding key
* @param maybeKey - key, that you use to create lists of elements
*/
function jsx(type, config, maybeKey) {
  // ...
}
```

### Use cases

createElement has two use cases: 
👉 manually create elements in your code
👉 transform JSX before React 17

jsx function should only be used by the compiler.
❌ You must not use it on your own.

### Dev mode

createElement internally makes a couple of checks to provide meaningful warnings in development mode.

jsx function instead has two separate versions:
👉 jsx for production mode
👉 jsxDEV for development mode

That's why jsx is cleaner and shorter than createElement

```js
// react/jsx-dev-runtime.js
export {jsxDEV} from './src/jsx/ReactJSX';

// =======================================

// react/jsx-runtime.js
export {jsx} from './src/jsx/ReactJSX';
```

### Fundamental similarities

Despite all differences, you need to keep in mind that both functions eventually use ReactElement().
So, the output is almost identical.

```js
export function createElement(type, config, children) {
  // ...
  
  return ReactElement(
    type,
    key,
    ref,
    self,
    source,
    ReactCurrentOwner.current,
    props,
  );
}

// ====================================================

export function jsx(type, config, maybeKey) {
  // ...
  
  return ReactElement(
    type,
    key,
    ref,
    undefined, // <- minor difference here
    undefined, // <- and here too
    ReactCurrentOwner.current,
    props,
  );
}
```

If you want code-to-code comparison, let me know 👇

---

P.S. [Follow me on Twitter](https://twitter.com/fromaline) for more content like this!

{% twitter 1484949311897096192 %}