Back

Unveiling HTM: A lightweight approach to building UI templates

Unveiling HTM: A lightweight approach to building UI templates

As this article will show, HTM is a new approach to easily and quickly developing web templates. It uses JavaScript’s template literals.

The ever-evolving landscape of web development demands efficient solutions. Crafting intricate user interfaces (UIs) often involves repetitive tasks and managing the intricate interplay between visual elements and underlying logic. This is where HTM (Hyperscript Tagged Markup) steps in, offering a lightweight and innovative approach to building UI templates. Imagine streamlining UI development. HTM leverages tagged template literals, a built-in JavaScript feature, allowing developers to define the structure of their UI elements using a familiar HTML-like syntax. This eliminates the need for complex templating languages or additional tools, fostering a direct and efficient workflow. Delving deeper, we’ll explore the key characteristics of HTM and its role in modern web development, highlighting its advantages and limitations. By the end, you’ll understand how HTM empowers developers to build UIs faster and with greater ease.

What is HTM?

HTM (Hyperscript Tagged Markup) stands out in the UI templating landscape. It is a lightweight templating language meticulously crafted to simplify the creation of UI components directly within JavaScript code. Its fundamental strength lies in harnessing JavaScript’s tagged template literals, eliminating the need for external tools or additional transpilation steps.

What are JavaScript’s tagged template literals?

Imagine strings enclosed in backticks (`) instead of quotes. These are template literals. They allow embedding expressions using the ${expression} syntax. The expression gets evaluated and inserted into the string. Let’s write a code that logs HELLO BOB where the value BOB is coming from a variable name

const name = "BOB";
console.log(`HELLO ${name}`); // Output: "Hello, Bob"

In the provided code snippet, the value of the variable name, set to BOB, is merged into the string HELLO, resulting in the output HELLO BOB appearing in the console. However, it’s important to distinguish between template literals and tagged template literals, especially in the context of HTM (Hyperscript Tagged Markup). While template literals allow for straightforward string interpolation, tagged template literals offer enhanced functionality.

Tagged template literals involve defining a function, commonly referred to as a tag function, which receives the template literal as its initial argument, along with any embedded expressions as subsequent arguments. This mechanism enables more sophisticated manipulation of the template content. For instance:

function fancyText(strings, ...values) {
  // Access the template's literal parts and expressions
  return {
    strings,
    values,
  };
}

const message = fancyText`This is a ${"styled"} message.`;
console.log(message); // Output: { strings: [ 'This is a ', ' message.' ], values: [ 'styled' ] }

Here, fancyText is a function that takes in two parameters, strings and ...values. strings, represents an array containing the string parts of the template literal passed to the function. Each element of this array corresponds to the literal text between the ${...} expressions in the template literal, while ...values is a rest parameter that collects all the expressions (interpolated values) in the template literal into an array called values.

In summary, HTM utilizes tagged template literals to articulate UI element structures directly within JavaScript code. The tag function deciphers the template literals and expressions, ultimately crafting the HTML code accordingly.

Key features of HTM

HTM, a template library for HTML, offers several key features, including clear syntax, dynamic content capabilities, and componentization. Delving into frameworks like Preact alongside these template libraries can deepen our understanding, much like how working with React enhances our grasp of JSX. Let’s embark on a straightforward project leveraging Preact to facilitate this exploration. We will focus on developing a greeting generator application aimed at dynamically generating and displaying greetings.

For this project, we’ll utilize Preact directly within the browser environment. We’ll create three essential files: index.html, main.js, and script.js. Additionally, we’ll incorporate Tailwind CSS for styling purposes, leveraging the Tailwind Play CDN. Here’s how our file structure will look:

Root directory
  |--index.html
  |--main.js
  |--script.js
  |
  |

Before delving into explaining the aforementioned features, let’s refine the index.html file.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>HTM Tutorial</title>
    <script src="https://cdn.tailwindcss.com"></script>
  </head>
  <body>
    <div id="app"></div>
    <script type="module" src="main.js"></script>
  </body>
</html>

The index.html file lays the groundwork for our Preact project, establishing the framework for our application and integrating essential dependencies. Notably, these dependencies include Tailwind CSS via CDN and main.js via URL. Below is the code snippet illustrating the integration of these dependencies:

Tailwind CSS

<script src="https://cdn.tailwindcss.com"></script>

This script tag imports the Tailwind CSS framework directly from the Tailwind Play CDN. Tailwind CSS is a utility-first CSS framework that provides pre-defined classes to quickly style HTML elements. By including this script, we can use Tailwind CSS classes in our HTML to style our application components.

main.js

<script type="module" src="main.js"></script>

This script tag incorporates a JavaScript file (in this instance, main.js) as a module within the browser environment.

Now, let’s dive into the features of HTM.

Clear syntax

To elucidate the syntax of HTM, let’s make modifications to the main.js and script.js files. Within these files, we’ll utilize HTM to craft the interface that will be rendered in the browser.

script.js file:

import { h } from "https://esm.sh/preact";
import htm from "https://esm.sh/htm";

// Initialize htm with Preact
export const html = htm.bind(h);

export default function App({ name }) {
  return html`<div class="flex items-center justify-center h-[100vh]">
    <h1 class="font-bold text-3xl">Hello ${name}!</h1>
  </div>`;
}

This component showcases the seamless integration of Preact elements with HTML templates using HTM. Let’s break down the code step by step:

  • Importing libraries: The import statement, specifically import { h } from "https://esm.sh/preact", retrieves the h function from the Preact library. This function is commonly used in Preact to create virtual DOM elements. Similarly, the import htm from "https://esm.sh/htm" statement imports the htm library, which provides a tagged template literal function used for generating HTML-like markup with JavaScript expressions.

  • Initializing HTM: We initialize HTM by binding the htm function to the h function from Preact using const html = htm.bind(h). This syntax ensures that HTM is configured to work with Preact’s virtual DOM elements. By exporting the html constant using export, we make it accessible for import in other modules, enabling its usage throughout the application.

  • Using the html function: Within the App component, we utilize the html function to define the structure of the HTML element to be rendered. Inside the template literal, we incorporate JavaScript expressions, such as ${name}, to dynamically insert values. Additionally, a <div> element is returned with Tailwind CSS classes applied to center its contents both horizontally and vertically within the viewport. Inside the <div>, an <h1> element displays the greeting Hello followed by the provided name.

By binding HTM to Preact’s h function, we seamlessly integrate HTML creation with Preact components, allowing for the creation of dynamic and interactive user interfaces within Preact applications.

main.js file

import { render } from "https://esm.sh/preact";
import App, { html } from "./script.js";

render(html`<${App} name="World" />`, document.getElementById("app"));

The provided code imports the render function from the Preact library, hosted at the specified URL https://esm.sh/preact, utilizing ES modules. It also imports the App component and html function from the local file script.js we have just created. Following the imports, it renders the App component with the attribute name set to World using the render function. The rendered output is then attached to the HTML element with the id app. Notably, the App component is constructed using the HTM syntax.

Upon execution, this code would yield a webpage resembling the one depicted in the provided image. Webpage

Dynamic content capabilities

To exemplify the dynamic capabilities of HTM, let’s infuse our project with dynamism by displaying multiple greetings on the browser. These greetings will elegantly unveil one after another as users scroll through the webpage. To accomplish this, let’s modify the script.js file.

import { h } from "https://esm.sh/preact";
import htm from "https://esm.sh/htm";

const listOfGreetings = [
  "World",
  "OpenReplay",
  "Uzoukwu",
  "Chinagorom",
  "EveryOne",
];

// Initialize htm with Preact
export const html = htm.bind(h);

function Greetings({ name }) {
  return html`<div class="flex items-center justify-center h-screen snap-start">
    <h1 class="font-bold text-3xl">Hello ${name}!</h1>
  </div>`;
}

export default function App() {
  return html`<div class="snap-y snap-mandatory h-screen overflow-y-scroll">
    ${listOfGreetings.map((i) => html`<${Greetings} name=${i} />`)}
  </div>`;
}

In this code snippet, the App component dynamically generates instances of the Greetings component for each item in the listOfGreetings array. As users scroll through the webpage, they are presented with a sequence of greetings that dynamically adapt based on the contents of the listOfGreetings array.

HTM plays a crucial role in enabling this dynamic rendering process. By utilizing HTM’s html function, we can seamlessly generate and render HTML content directly within our Preact application. This allows for the creation of dynamic user interfaces with ease, enhancing the overall user experience.

Let’s delve deeper into how HTM facilitates this dynamic rendering process. Within the template literal, we incorporate JavaScript expressions like ${listOfGreetings.map((name) => html`<${Greetings} name=${name} />`). This expression dynamically generates instances of the Greetings component for each item in the listOfGreetings array. Each instance of the Greetings component is tailored with a different name prop, enabling dynamic content rendering. Consequently, HTM seamlessly integrates dynamic content into the HTML markup, allowing for the rendering of multiple greetings based on the contents of the listOfGreetings array.

When executed in the browser, the result is as follows: Completion

Componentization

The script.js file exemplifies the componentization capability of HTM. The App component serves as a container for rendering multiple instances of the Greetings component, each with a different name prop. Here’s how it demonstrates componentization:

  • Container component (App): The App component manages the rendering of multiple Greetings component instances. It leverages the html function from HTM to generate HTML markup with embedded JavaScript expressions.

  • Reusable component (Greetings): The Greetings component is designed to render greeting messages and is reusable across the application. It accepts a name prop, dynamically determining the content of the greeting message.

  • Dynamic rendering: In the App component’s template literal, a JavaScript expression iterates through the listOfGreetings array using the map method. Each iteration dynamically generates a new instance of the Greetings component, passing the name prop to facilitate dynamic content rendering based on the array data.

  • Encapsulation and reusability: Encapsulating rendering logic in the App component and utilizing the reusable Greetings component enhances code organization and maintainability. This modular approach promotes code encapsulation and reusability, facilitating easier integration into other parts of the application.

In summary, HTM empowers componentization by enabling the creation of reusable UI components like Greetings. These components can be dynamically integrated within container components such as App, promoting modularity and enhancing code organization. This modular approach not only fosters reusability but also contributes to a more maintainable and scalable codebase.

Advantages of HTM

Performance

HTM’s lightweight nature translates to faster rendering and improved performance. By minimizing the overhead associated with more complex templating solutions, HTM enables snappier user experiences and smoother interactions.

No transpilation

One of HTM’s key advantages is its simplicity. Developers can write HTML-like markup directly within JavaScript code, bypassing the need for transpilation steps such as JSX to JavaScript. This streamlined approach simplifies the development workflow and reduces build times.

Simplicity

HTM’s syntax closely resembles HTML, making it intuitive and easy to learn, especially for developers already familiar with web development fundamentals. Its straightforward syntax lowers the barrier to entry, allowing developers to quickly get up to speed and start building UI components with a minimal learning curve.

Limitations of HTM

Tooling support

While HTM offers simplicity, it may lack robust tooling support compared to more established template languages like JSX. Developers may find limited IntelliSense and ESLint support, which could impact code quality and development productivity. However, community-driven efforts may address these shortcomings over time.

Readability

As applications grow in complexity, deeply nested HTM structures may become less readable and harder to maintain. Without proper organization and modularization of components, code readability could suffer, leading to potential maintenance challenges. Developers should prioritize clear code architecture and component design to mitigate readability issues.

Browser compatibility

While HTM is designed to work across modern browsers, compatibility issues may arise with older or less common browser versions. Advanced HTM features may not be fully supported in all environments, requiring developers to implement fallbacks or alternative solutions to ensure consistent behavior across platforms.

While HTM offers advantages such as performance optimization, simplicity, and ease of use, developers should be mindful of potential limitations such as tooling support, readability concerns, and browser compatibility. By understanding these factors and leveraging HTM’s strengths effectively, developers can harness its power to build efficient, maintainable, and responsive user interfaces for web applications.

Conclusion

In conclusion, HTM offers a lightweight, straightforward approach to UI templating, with advantages including improved performance, simplicity, and ease of use. While it may have limitations in terms of tooling support and browser compatibility, HTM is well-suited for scenarios where performance and simplicity are paramount. Whether you’re building a small project or a large-scale application, HTM provides a compelling option for creating dynamic and responsive user interfaces.

Gain Debugging Superpowers

Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers. Check our GitHub repo and join the thousands of developers in our community.

OpenReplay