DEV Community

Cover image for JSX without importing React
Nick
Nick

Posted on • Updated on

JSX without importing React

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

// 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!');
}
Enter fullscreen mode Exit fullscreen mode

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.

// 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!'});
}
Enter fullscreen mode Exit fullscreen mode

createElement ๐Ÿ†š jsx

createElement vs jsx

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.

/**
* @param type - type of the element
* @param config - props passed to the element
* @param children - children of the element
*/
function createElement(type, config, children) {
  // ...
}
Enter fullscreen mode Exit fullscreen mode

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.

/**
* @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) {
  // ...
}
Enter fullscreen mode Exit fullscreen mode

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

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

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

// react/jsx-runtime.js
export {jsx} from './src/jsx/ReactJSX';
Enter fullscreen mode Exit fullscreen mode

Fundamental similarities

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

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,
  );
}
Enter fullscreen mode Exit fullscreen mode

If you want code-to-code comparison, let me know ๐Ÿ‘‡


P.S. Follow me on Twitter for more content like this!

Latest comments (3)

Collapse
 
ashishk1331 profile image
Ashish Khare๐Ÿ˜Ž

So this opens a brand new way of creating components, like a file may contain different
functions which in turn encapsulate the different component's structures and abiding behaviors. At last all can be called upon by a main function. Can I say this will help in micro-dividing the functions( components ) without wrapping each of its body part in a separate react bracket? Am I right? Also, it would help in avoiding to write the imports for just buttons. I'm eager to know what else can be done?

Collapse
 
fromaline profile image
Nick

I don't fully understand what you mean.
Shift from createElement() to jsx() doesn't provide new functionality yet. It just gives the ability to write jsx without React in scope.

What is react bracket?

Collapse
 
ashishk1331 profile image
Ashish Khare๐Ÿ˜Ž

Okay! I got it all wrong. Sorry for that.
Now I read it twice and correctly.