JSX for web components
Tags: webdev, webcomponents, jsx
Published: 2021-08-21 05:40:44

Love it or hate it, JSX is a great technology for rendering HTML using Javascript. It is available for most frameworks but did you know that you can use it for rendering web components too?

You can use JSX with or without transpiling your code.

## Without transpiling

[Tagged template literals](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals) can be used directly in the browser. The [htm](https://www.npmjs.com/package/htm) library provides a JSX-like tagged template literal for rendering HTML.

Here is how you can use it in a [FicusJS](https://docs.ficusjs.org/) web component.

```javascript
import { createCustomElement } from 'ficusjs/custom-element'
import { html, renderer } from '@ficusjs/renderers/htm'

createCustomElement('hello-world', {
    renderer,
    render () {
        return html`<p>Hello world!</p>`
    }
})
```

Template literals are literals delimited with backticks, allowing embedded expressions called substitutions. Tagged template literals call a function (in this case the `html` tag function) with an array of any text segments from the literal followed by arguments with the values of any substitutions. 

This example uses the [`@ficusjs/renderers`](https://github.com/ficusjs/ficusjs-renderers) package which provides a browser-ready ES module using the `htm` library.

Clone this Snowpack starter to see it in action - [https://github.com/ficusjs/ficusjs-snowpack-starter](https://github.com/ficusjs/ficusjs-snowpack-starter)

## With transpiling

As JSX extends Javascript, one of its best features is the ability to transpile it.

Transpiling removes the overhead of loading the renderer and parsing the JSX at runtime. It would be used as a production step to remove the renderer.

The [`babel-plugin-htm`](https://github.com/developit/htm/tree/master/packages/babel-plugin-htm) Babel plugin compiles tagged template literals using `htm` to hyperscript. This is the same process React/Preact use in transpiling JSX for production.

Here is an example.

```javascript
import { h } from '@ficusjs/renderers/jsx-dom'
import { createCustomElement } from 'ficusjs/custom-element'

createCustomElement('hello-world', {
    render () {
        return html`<p>Hello world!</p>`
    }
})
```

It is very similar to the without transpilation example except that no `renderer` function is imported and the `h` pragma is imported for transpiling the JSX.

### What is the `h` pragma?

By default, Babel will transform `<p>Hello world!</p>` to `React.createElement("p", null, "Hello world!");`. Pragma `h` will generate instead as `h("p", null, "Hello world!");`.

Pragma is the name to describe the function used by Babel to transpile the JSX.

In this example, pragma `h` is the function used by Babel to transpile JSX to Javascript to build the DOM rendered by the component.

By default, the [FicusJS](https://docs.ficusjs.org/) web component `render` function expects a DOM element for rendering. The pragma imported from the `@ficusjs/renderers/jsx-dom` library creates a DOM element for rendering in the component.

This is the output of the transpilation.

```javascript
import { h } from './_snowpack/pkg/@ficusjs/renderers/jsx-dom.js';
import { createCustomElement } from './_snowpack/pkg/ficusjs/custom-element.js';
createCustomElement('hello-world', {
  render() {
    return h("p", null, "Hello world!");
  }
});
```

Clone this Snowpack starter to see JSX transpilation in action - [https://github.com/ficusjs/ficusjs-compiled-templates](https://github.com/ficusjs/ficusjs-compiled-templates)

## Summary

Using JSX in your web components allows you to use tagged template literals in development and then transpile it away for production resulting in smaller optimised components.

The [htm](https://github.com/developit/htm) library gives you JSX-like syntax but goes even further.

- Spread props: `<div ...${props}>` instead of `<div {...props}>`
- Self-closing tags: `<div />`
- Components: `<${Foo}>` instead of `<Foo>` (where `Foo` is a component reference)
- Boolean attributes: `<div draggable />`
- **No transpiler necessary**
- HTML's optional quotes: `<div class=foo>`
- Component end-tags: `<${Footer}>footer content<//>`
- Syntax highlighting and language support via the [lit-html VSCode extension](https://marketplace.visualstudio.com/items?itemName=bierner.lit-html) and [vim-jsx-pretty plugin](https://github.com/MaxMEllon/vim-jsx-pretty)
- Multiple root element (fragments): `<div /><div />`
- Support for HTML-style comments: `<div><!-- comment --></div>`

Try it in your next project!