DEV Community

Cover image for Hyperscript - the hidden language of React
Nick
Nick

Posted on

Hyperscript - the hidden language of React

JSX is the starting point

React uses JSX to make things easier for the developers. So when you write something like this.

<div id="foo">
  Hello!
</div>
Enter fullscreen mode Exit fullscreen mode

Babel with a react preset transforms it to this.

React.createElement("div", {
  id: "foo"
}, "Hello!");
Enter fullscreen mode Exit fullscreen mode

Check out this example in Babel REPL.
React.createElement is a function that creates a virtual node.
It's a well-known fact, and you probably already know it. So what's the point?

Preact way

If you've used Preact before, you may notice it has an unobvious export in its source code.

export {
        createElement,
        createElement as h,
} from './create-element';
Enter fullscreen mode Exit fullscreen mode

To make things clear, the createElement function from Preact serves the same needs as React.createElement. So the question is, why is it exported as h as well?

The reason is dead simple. It's exported as h because it's a hypescript function.
So what exactly is hypescript?

Hyperscript is the key

Hypescript is a kind of language to create HyperText with JavaScript and was started by Dominic Tarr in 2012. He was inspired by markaby, the "short bit of code" to write HTML in pure Ruby.
Markaby allows doing things like that.

require 'markaby'

mab = Markaby::Builder.new
mab.html do
  head { title "Boats.com" }
  body do
    h1 "Boats.com has great deals"
    ul do
      li "$49 for a canoe"
      li "$39 for a raft"
      li "$29 for a huge boot that floats and can fit 5 people"
    end
  end
end
puts mab.to_s
Enter fullscreen mode Exit fullscreen mode

And the h function allows doing essentially the same thing, but with different syntax.

h = require("hyperscript")

h("div#foo", "Hello!")
Enter fullscreen mode Exit fullscreen mode

It also supports nesting and CSS properties.

h = require("hyperscript")

h("div#foo", 
  h("h1", "Hello from H1!", { style: { 'color': 'coral' } })
)
Enter fullscreen mode Exit fullscreen mode

Check out an interactive demo to see how it works.

Get your hands dirty

Now when we know what the h function does and why we need it, let's write our own version of it. Complete example can be found on codesanbox.

First, let's make up a render function, that creates real DOM elements from our virtual nodes.

const render = ({type, children, props}) => {
  const element = document.createElement(type);

  if (props) {
    for (const prop in props) {
      element.setAttribute(prop, props[prop]);
    }
  }

  if (children) {
    if (Array.isArray(children)) {
      children.forEach(child => {
        if (typeof child === 'string') {
          element.innerText = child;
        } else {
          element.appendChild(render(child));
        }
      })
    } else if (typeof children === 'string') {
      element.innerText = children;
    } else {
      element.appendChild(render(children));
    }
  }

  return element;
}
Enter fullscreen mode Exit fullscreen mode

Than, let's create the h function.

const h = (type, children, props) => {
  let handledType = typeof type === 'string' ? type : 'div';

  return {
    type: handledType,
    props,
    children
  }
}
Enter fullscreen mode Exit fullscreen mode

Finally, let's create an actual content with our h function, render it with our render function and mount the result to the DOM.

const div = render(
  h('div',
    [ 
      h('h1', 'Hello!', { id: 'foo' }),
      h('h2', 'World!', { class: 'bar' })
    ],
  )
);

document.querySelector('#app').appendChild(div);
Enter fullscreen mode Exit fullscreen mode

Top comments (3)

Collapse
 
artydev profile image
artydev

Thank you :-)

Collapse
 
karsonkalt profile image
Karson Kalt

Great read! Thank you!

Collapse
 
fromaline profile image
Nick

You're welcome!
If you like it, check out my new post, where we take a deep dive into React source code!