Skip to content
TwitterDiscordGitHub

Introduction

🌐 HMPL (/ˈhæmpəl/ portmanteau of the words Cample and HTML) is a small template language for displaying UI from server to client. It is based on customizable requests sent to the server via fetch and processed into ready-made HTML. The language is syntactically object-based and integrated with JSON5 and DOMPurify. Reduce the size of your javascript files and display the same UI as if it was written in a modern framework!

The HTML you use on your site is enhanced by adding special blocks that resemble components in syntax, but are more like “helper blocks” like in Handlebars.

In these blocks you describe the request that will be sent to the server to get the finished components from there. This includes HTTP methods, request frequency and many other things that are used when talking about fetch.

To describe it more briefly, it is something in between HTMX and EJS 👀.

Unlike other similar modules, the template language provides more complete customization due to a well-thought-out balance between HTML and JS. HMPL supports modern EcmaScript standards - thanks to its well-thought-out functionality, you will not depend on versions.

Also, the template language format itself allows you to work with .hmpl extension files if you use Vite or WebPack.

HMPL provides a set of tools to streamline development and integration with popular editors and build systems. These tools are designed to simplify working with .hmpl files, enabling developers to write, compile, and manage HMPL projects efficiently and consistently.


The official HMPL tools include support for VSCode, Vite, and Webpack, offering features such as syntax highlighting, autocompletion, hot module replacement, and seamless compilation. Together, they ensure a smooth development experience and help maintain high-quality code across projects.

VSCode Logo
VSCode ExtensionSyntax highlighting and tools for HMPL.
Vite Logo
Vite PluginSeamless .hmpl integration with Vite.
Webpack Logo
Webpack LoaderCompile .hmpl files inside Webpack.

You may have questions about the template language, here is a short list of those that were asked throughout the development cycle:

Is it necessary to use JavaScript?

No, you don’t have to use it with the hmpl-dom module, but the template language itself is fully configured for this.

Is it safe?

Yes, the template language is completely safe, as it only accepts text/html by default. If you are working with an uncontrolled API, the module also provides protection against XSS attacks. More information about security can be found here.

What is the difference between HTMX and Alpine.js?

We have written several articles on this topic, specifically about the differences from HTMX and Alpine.js

Can I use HMPL with modern frameworks?

Yes! HMPL is designed to work alongside modern frameworks. It’s something in between HTMX and EJS, providing a lightweight alternative for server-client communication without the overhead of full framework implementations.

<template data-hmpl data-config-id="clicker-config">
<div>
<button data-action="increment" id="btn">Click!</button>
<div>
Clicks: {{#request src="/api/clicks" after="click:#btn"}}{{/request}}
</div>
</div>
</template>
<script src="https://unpkg.com/json5/dist/index.min.js"></script>
<script src="https://unpkg.com/dompurify/dist/purify.min.js"></script>
<script src="https://unpkg.com/hmpl-js/dist/hmpl.min.js"></script>
<script src="https://unpkg.com/hmpl-dom/dist/hmpl-dom.min.js"></script>
<span class="counts"><?php $clicks ?></span>
<div>
<button data-action="increment" id="btn">Click!</button>
<div>Clicks: <span class="counts">0</span></div>
</div>
import { compile } from "hmpl-js";
const templateFn = compile(
`<div>
<form onsubmit="function prevent(e){e.preventDefault();};return prevent(event);" id="form">
<div class="form-example">
<label for="login">Login: </label>
<input type="login" name="login" id="login" required />
</div>
<div class="form-example">
<input type="submit" value="Register!" />
</div>
</form>
<p>
{{#request
src="/api/register"
after="submit:#form"
repeat=false
indicators=[
{
trigger: "pending",
content: "<p>Loading...</p>"
}
]
}}
{{/request}}
</p>
</div>`
);
const initFn = (ctx) => {
const event = ctx.request.event;
return {
body: new FormData(event.target, event.submitter),
credentials: "same-origin"
};
};
const obj = templateFn(initFn);
const wrapper = document.getElementById("wrapper");
wrapper.appendChild(obj.response);