I’m personally a big fan of JSX and love the way it allows me to split up and componentize my code. Even though JSX had been around before React, it wouldn’t have been nearly as popular without React picking it up. However, we can actually use JSX without React, and it’s not that difficult either.

The way React works is by configuring your bundler to convert JSX into calls to a createElement function. So for example:

const foo = (
		<div className="cool">
			<p>Hello there!</p>
		</div>
	)

	// Would become this:
	React.createElement(
		'div',
		{ className: 'cool' },
		React.createElement('p', null, 'Hello there!')
	)
view raw
block1.jsx hosted with ❤ by GitHub

However, most bundlers allow you to choose your own JSX_ pragma_ (function that will be in the place of React.createElement). For example, if you were using Babel, you could specify what function to use through a simple comment like this:

/** @jsx myJsxFunction */

	const foo = (
		<div className="cool">
			<p>Hello there!</p>
		</div>
	)
view raw
block2.jsx hosted with ❤ by GitHub

And now Babel would pass those some parameters to myJsxFunction. Now all we need to do is create a function that takes these parameters and creates real DOM nodes that we can add to our DOM. So let’s get started. (If you need a code sandbox to play around in, you can use this static template using standalone Babel.)

DOM nodes are created using the document.createNode() function. It requires just a tag name, so a good place to start would be with that:

export const createElement = (tag, props, ...children) => {
		const element = document.createElement(tag)
		return element
	}
view raw
block3.jsx hosted with ❤ by GitHub

Now that we have a DOM node, we have to actually add the attributes provided to us. These can be anything like class or style. So we’ll just loop through all the provided attributes (using Object.entries) and just set them on our DOM node:

export const createElement = (tag, props, ...children) => {
		const element = document.createElement(tag)

		Object.entries(props || {}).forEach(([name, value]) => {
			element.setAttribute(name, value.toString())
		})

		return element
	}
view raw
block4.jsx hosted with ❤ by GitHub

This approach has one issue, though. How do we handle events? For example, let’s say I have this JSX:

const SayHello = (
		<div>
			<button onClick={() => console.log('hello there!')}>Say Hello</button>
		</div>
	)
view raw
block5.jsx hosted with ❤ by GitHub

Our function would set onClick as a normal attribute with the callback set as actual text.

Instead what we can do is check if our attribute starts with on and is in the window scope. This will tell us if it’s an event or not. For example, onclick is in the window scope; however, onfoo isn’t. If it is, then we can register an event listener on that node using the part of the name without the on.

#nodejs #html

How to Use JSX Without React
25.65 GEEK