DEV Community

Matt Levy for Ficus

Posted on

JSX for web components

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 can be used directly in the browser. The htm library provides a JSX-like tagged template literal for rendering HTML.

Here is how you can use it in a FicusJS web component.

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

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

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 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

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 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.

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

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

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 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.

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!");
  }
});
Enter fullscreen mode Exit fullscreen mode

Clone this Snowpack starter to see JSX transpilation in action - 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 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 and vim-jsx-pretty plugin
  • Multiple root element (fragments): <div /><div />
  • Support for HTML-style comments: <div><!-- comment --></div>

Try it in your next project!

Discussion (0)