React without JSX and Webpack
React without JSX? Does that even exist? Yes and it's actually a more viable (and very easy-to-use) option than you might think!
What is the Idea behind JSX?
Before we dive into React without JSX, let me quickly clarify what JSX actually is and why React uses it.
By the way, in case you're not a reader - you can also watch the video on top of this page!
So what's JSX?
Well, here's a snippet:
const myComponent = props => {return <div>I'm JSX!</div>}
Here, we create a very simple functional React component which renders a <div>
with some text to the screen.
JSX looks like HTML, but actually, we're just writing JavaScript code in that snippet!
HTML tags are not natively supported in JavaScript though, hence the code in the snippet only executes if it's transpiled to "regular" JavaScript.
JSX is just syntactical sugar! It doesn't run natively in the browser and React does not depend on it!
You simply use JSX in React projects because you can write your rendered HTML code as - well - HTML code. This of course means, that you could also just skip the JSX step and instead directly write the code to which it would get compiled to.
What JSX gets transpiled to
So what does JSX get transpiled to?
Here's the snippet from before in the version as it executes in the browser:
const myComponent = props => {return React.createElement('div', null, "I'm JSX")}
Here, we use a method provided by React: createElement
. You therefore need to import React into your JS file:
import React from 'react'
This is the reason why you have to add this import to your JS files where you use JSX, too. Did you ever wonder why you needed it, even though you did never use the React object? Well, JSX and the code it gets transpiled to (which does use the React object) is the reason.
Let's take a closer look at createElement
.
We pass three arguments to it:
'div'
: This tells React which HTML element you eventually want to render to the DOMnull
: This defines which properties you want to set on the created element (e.g. add a CSS class)'I\'m JSX'
: This defines what goes between the HTML tags (i.e. between<div>
and</div>
in this case)
You're not limited to HTML elements
You're also not limited to HTML tags!
Indeed, you'll also create elements based on your own components quite a lot - just as you do in JSX!
import React from 'react'const anotherComponent = props => {return React.createElement(myComponent, null, null)}
In this snippet, we create another React element - this time based on the myComponent
functional component that was defined earlier in this article.
So React.createElement
works with both: HTML tags and custom components - the custom components can be functional ones (as shown in this article) but of course also class-based (stateful) ones.
From React element to the DOM
Okay, so we have these React elements which we create with React.createElement
. But what is that actually and how do we render it to the DOM?
You might recall that vanilla JS has a createElement
method, too:
document.createElement('div') // This creates a <div>
React.createElement
essentially creates a JS object with all the configuration required to render it to the real DOM. And that rendering is done with the help of another package from the React universe: ReactDOM.
ReactDOM.render(React.createElement(anotherComponent, null, null), document.getElementById('app')
This will render the newly created React element into a hook provided by us in our HTML page into which this script + the React dependency gets imported:
<body><div id="app"></div><script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script><script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script><script src="app.js"></script></body>
This setup requires absolutely no build workflow since we import and use vanilla JS only. No syntactical sugar at all!
What about props?
Thus far, we created a lot of elements with React.createElement
but the second argument always was null
.
It could look like this:
React.createElement('div', { className: 'some-css-class' }, 'Some content')
This would lead to this HTML element being created:
<div class="some-css-class">Some Content</div>
Essentially, the second argument allows you to bind properties (or "pass props") to the element you're creating.
So the JSX equivalent would be this:
<div className="some-css-class">Some Content</div>
className
is required because class
is a reserved keyword in JavaScript.
Of course, you can also pass your own props to your own components. Here's a JSX example followed by the JSX-less alternative:
<Person name="Max" age={28} />
React.createElement(Person, { name: 'Max', age: 28 }, null)
Should you use it?
The obvious question now is: Should you use this JSX-less approach over the JSX-focused approach you normally see in all React projects?
You probably want to stick to JSX for your medium-sized or bigger projects. It still helps to understand what happens under the hood.
For smaller React projects, where you don't want to add a complex build workflow, or for multi-page-applications, you could consider the JSX-less version though.
It gets you up and running quickly and you only need two React imports (React
and ReactDOM
) as well as your own script file and you're ready to go. Doesn't sound like the worst alternative!