Template Literals

Many people really dislike JSX. At the same time, some people also do not like having to define markup with the h function. For them there is another way: ES6 template literals. It is possible to use template literals to define what the render function returns, but he requires a litter setup.

Hyperx-es6

There is a module on NPM called Hyperx that lets you use ES6 template literals and converts them to hyperscript functions. Unfortunately it does not work with Rollup for ES6 module imports. We therefore forked it as an ES6 module and called it: Hyperx-es6. To use it you'll have to install it.

Open your terminal and cd to your project. Then install Hyperx-es6 as a dependency:

npm i -D hyperx-es6

With Hyperx-es6 installed, you can now use it with your components. To do so, you'll need to import it into where you want to use it. You still need to import the h funtion. When Hyperx-es6 transpiles the template literals, it will use the h function to convert the result into virtual nodes that Composi understands:

import { h, Component } from 'composi'
import { hyperx } from 'hyperx-es6'

// Tell Hyperx-es6 to use the Composi h function:
const html = hyperx(h)

We can now use the html function to define template literals as markup for Composi components. If you have not used template literals before, you might want to read up on how they work.

Using Template Literals

You can use template literals just as you normally would. Whereas JSX uses {someVariable} to evaluate variables, template literals use curly braces preceded by $: ${someVariable}. Notice how we capture Hyperx-es6 as a tagged template literal function `html`, which we then use to define markup:

import { h, Component } from 'composi'
import { hyperx } from 'hyperx-es6'
const html = hyperx(h)

// Use class attribute as normal:
function Header(data) {
  return html`
    <header>
      <h1 class='title'>${data.title}</h1>
      <h2 class='subtitle'>${data.subtitle}</h2>
    </header>
  `
}

Partial Attributes

Unlike JSX, Hyperx-es6 does support partial attribute values. The following code will work without a problem:

import { h, Component } from 'composi'
import { hyperx } from 'hyperx-es6'
const html = hyperx(h)

function UserList(users) {
  return html`
    <ul>
       {
         users.map(user => <li class='currently-${user.employed ? "employed" : "unemployed"}'>${user.name}</li>)
       }
    </ul>
  `
}

Hyperx-es6 with Components

We can use Hyperx-es6 directly inside a Component as part of the render function. Notice that when we need to loop over the arrray of items, we use html to define the list item. If we instad made that part a template literal, the markup would be returned as an escaped string. Of course, if that is what you want, that is how you would do it.

class FruitsList extends Component { 
  render(fruits) { 
    return html` 
      <ul class='list'> 
        ${ 
          fruits.map(fruit => 
            html`<li>
              <div> <h3>{fruit.title}</h3> 
              <h4>{fruit.subtitle}</h4> 
            </div> 
            <aside> 
              <span class='disclosure'></span>
            </aside> 
          </li>`) 
        } 
      </ul> 
    ` 
  } 
}

Custom Tags

JSX has the concept of custom tags that allow you to break up complex markup into smaller, reusable pieces. You can accomplish the same thing with Hyperx-es6. Define the functions that return the markup and then use them inside the parent render function as methods inside dollar sign curly braces:

function listItem(fruit) { 
  return html`
    <div>
      <h3>${fruit.name}</h3>
      <h4>${fruit.price}</h4>
    </div>
  `
}

// Function to return static markup:
function disclosure() { 
  return html`
    <aside>
      <span class='disclosure'></span>
    </aside>`
}

//Now that we have some custom tags, we can use them as follows:

class FruitsList extends Component {
  render(fruits) {
    return html`
      <ul class='list'>
        {
          fruits.map(fruit => (
            <li>
              ${listItem(fruit)}
              ${disclosure()}
            </li> 
          )
        }
      </ul>
    `
  }
}

About Sibling Tags

Like JSX, you markup must always have one enclosing tag. Although it is legal to return a set of sibling elements in an ES6 template literal, this will not work with Composi's `h` function. That's because each tag you define will be converted into a function. As such, there needs to be one final, top-most function that returns the markup.

Bad markup:


// The following code cannot compile:
class BadHyperx extends Component {
  render() { 
    return html`
      <li>One</li>
      <li>Two</li>
      <li>Three</li>
    `
  }
}

The above code will not build. Instead you need to create the entire list like this and insert it in some higher element as the container:

class GoodHyperx extends Component {
  render() {
    return html`
      <ul>
        <li>One</li>
        <li>Two</li>
        <li>Three</li>
      </ul>
    `
  }
}