React without JSX

Table of contents

Each JSX element is just syntactic sugar for calling React.createElement(component, props, …children).

I removed the JSX from the boilerplate of the CRA app, and I replaced it with createElement calls for practice. Yes, but why?—you may ask. Here are some advantages of using createElement over JSX:

  • No build process is required for transpiling JSX.
  • See React from a different perspective.

And here are some disadvantages:

  • Hard to read.
  • Arguably harder to write.

Some useful resources:

The code

src/index.js

src/index.js
import { createElement as e } from "react";
import { render } from "react-dom";

import App from "./App";
import "./index.css";

render(e(App), document.getElementById("root"));

src/App.js

src/App.js
import { createElement as e } from "react";

import Button from "./components/Button";
import logo from "./logo.svg";
import "./App.css";

function App() {
  return e(
    "div",
    { className: "App" },
    e(
      "header",
      { className: "App-header" },
      e("img", { className: "App-logo", src: logo, alt: "logo" }),
      e(
        "p",
        null,
        "Edit ",
        e("code", null, "src/App.js"),
        " and save to reload."
      ),
      e(
        "a",
        {
          className: "App-link",
          href: "https://reactjs.org",
          target: "_blank",
          rel: "noopener noreferrer",
        },
        "Learn React"
      ),
      e(
        Button,
        {
          style: {
            backgroundColor: "rebeccapurple",
            marginTop: "32px",
            fontSize: "20px",
          },
          className: "my-button",
          onClick: (e) => console.log(e.target),
          // children: [
          //   e("strong", { key: null }, "Click "),
          //   "me ",
          //   e("i", { key: null }, "NOW!")
          // ]
        },
        e("strong", null, "Click "),
        "me ",
        e("i", null, "NOW!")
      )
    )
  );
}

export default App;

And this is a React app without a build process (bad idea). You still need some babel helpers though if you want to create reusable components.

index.html

index.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta
      name="viewport"
      content="width=device-width, initial-scale=1.0"
    />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Hello World</title>
    <link
      rel="stylesheet"
      href="https://unpkg.com/normalize.css@7.0.0/normalize.css"
    />
    <style>
      * {
        box-sizing: border-box;
      }
      body {
        margin: 0;
        font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
        font-size: 20px;
      }
      h1,
      h2,
      h3,
      h4,
      h5,
      h6 {
        font-family: Georgia, "Times New Roman", Times, serif;
      }
    </style>
  </head>
  <body>
    <div id="root"></div>
    <script
      crossorigin
      src="https://unpkg.com/react@16/umd/react.production.min.js"
    ></script>
    <script
      crossorigin
      src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"
    ></script>
    <script src="/babel-helpers/object-spread.js"></script>
    <script src="/babel-helpers/object-without-properties.js"></script>
    <script>
      var e = React.createElement;
      function Button(props) {
        var style = props.style;
        var restProps = _objectWithoutProperties(props, ["style"]);
        return e(
          "button",
          _objectSpread(
            {},
            {
              style: _objectSpread(
                {},
                {
                  padding: ".5em 1em",
                  cursor: "pointer",
                  border: 0,
                  borderRadius: "2px",
                  backgroundColor: "rebeccapurple",
                  color: "white",
                },
                style
              ),
            },
            restProps
          )
        );
      }
      var App = function () {
        return e(
          "div",
          {
            style: {
              minHeight: "100vh",
              backgroundColor: "#1d1d1d",
              color: "white",
              display: "flex",
              alignItems: "center",
              justifyContent: "center",
              flexDirection: "column",
            },
          },
          e(
            "div",
            { style: { padding: "16px" } },
            e(
              "h1",
              { style: { fontSize: "3em", textAlign: "center" } },
              "Hello World!"
            ),
            e(
              "p",
              { style: { maxWidth: "400px", marginBottom: "40px" } },
              "Lorem ipsum dolor sit amet consectetur adipisicing elit. Cumque minima temporibus tenetur natus aspernatur dolore eius adipisci quo neque perspiciatis!"
            ),
            e(
              Button,
              {
                onClick: function (e) {
                  console.log(e.target);
                },
                style: {
                  backgroundColor: "#de2b61",
                  float: "left",
                },
              },
              "Click me"
            )
          )
        );
      };
      ReactDOM.render(e(App), document.getElementById("root"));
    </script>
  </body>
</html>

Here I use the objectSpread to combine the Button props into one object as well as its style prop into a different one. I’m also using the objectWithoutProperties to extract the style from the rest of the props.

Other things to read

Popular

Previous/Next