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.
Discover how at OpenReplay.com.
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, specificallyimport { h } from "https://esm.sh/preact"
, retrieves theh
function from the Preact library. This function is commonly used in Preact to create virtual DOM elements. Similarly, theimport htm from "https://esm.sh/htm"
statement imports thehtm
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 theh
function from Preact usingconst html = htm.bind(h)
. This syntax ensures that HTM is configured to work with Preact’s virtual DOM elements. By exporting thehtml
constant usingexport
, we make it accessible for import in other modules, enabling its usage throughout the application. -
Using the
html
function: Within theApp
component, we utilize thehtml
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 greetingHello
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.
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:
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
): TheApp
component manages the rendering of multipleGreetings
component instances. It leverages thehtml
function from HTM to generate HTML markup with embedded JavaScript expressions. -
Reusable component (
Greetings
): TheGreetings
component is designed to render greeting messages and is reusable across the application. It accepts aname
prop, dynamically determining the content of the greeting message. -
Dynamic rendering: In the
App
component’s template literal, a JavaScript expression iterates through thelistOfGreetings
array using themap
method. Each iteration dynamically generates a new instance of theGreetings
component, passing thename
prop to facilitate dynamic content rendering based on the array data. -
Encapsulation and reusability: Encapsulating rendering logic in the
App
component and utilizing the reusableGreetings
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.