Skip to content

Introduction

Lift HTML is a tiny library for building HTML Web Components - components that enhance existing HTML on the page instead of rendering it on the client or hydrating it.

It seems like in modern web development you have to make a choice between huge JavaScript-centric framework or haphazard script tags thrown around the page.

And if you want something simpler you can’t have nice things like IDE integration or HMR.

And if you want something server-driven you have to write everything as string attribute and send huge unoptimized bundles to the client.

Lift HTML is aiming to close the gap between those extremes.

First of all Lift HTML doesn’t try to replace the tools you already use.

On the server you can continue to generate your HTML as usual. Types for your Lift HTML components are declared in TypeScript so you will have great time with tools that can use that for “Go to definition” and type-checking attributes (Astro, React, Solid and some other already supported, more tool support is planned).

On the client you don’t have to stop using your favorite JS tooling, we already implemented integrations with SolidJS Signals and Alien Signals that could help you drive the state of your components, as well as option to render markup using your favorite library (see example using render from SolidJS).

If you already have build pipeline set up for your project you can use it to get fanicer features like HMR and dead-code elimination. If you don’t you can safely use CDN imports and enjoy no-build development and know that you are not sending dozens of kilobytes to the client.

Lift HTML follows the HTML Web Components pattern, which means:

  • HTML is rendered on the server - Your components enhance existing HTML rather than rendering it
  • Progressive enhancement - Components add interactivity to static HTML
  • Minimal overhead - Start with as little as 150 bytes gzipped
  • Framework-agnostic - Works with any existing setup
  • Type-safe - Full TypeScript support out of the box

The core package providing the basic liftHtml function for creating web components. Perfect for simple components that don’t need reactive state management.

Bundle size: ~600 bytes gzipped

Includes the core functionality plus SolidJS integration for reactive components. Provides liftSolid function and useAttributes helper.

Bundle size: ~3.4kb gzipped (includes solid-js)

Minimal package for the most basic use cases. Provides just the essential functionality.

Bundle size: ~150 bytes gzipped

Get started with Lift HTML in just a few steps:

Terminal window
npm install @lift-html/core
import { liftHtml } from "@lift-html/core";
const CopyButton = liftHtml("copy-button", {
init() {
const button = this.querySelector("button");
if (!button) throw new Error("<copy-button> must contain a <button>");
const textToCopy = this.getAttribute("text") ?? this.textContent?.trim() ??
"";
button.disabled = false;
const setLabel = (label) => (button.textContent = label);
setLabel("Copy");
button.onclick = async () => {
try {
await navigator.clipboard.writeText(textToCopy);
setLabel("Copied!");
setTimeout(() => setLabel("Copy"), 1500);
} catch {
setLabel("Failed");
}
};
},
});
<copy-button text="Hello, world!">
<button disabled>Copy</button>
</copy-button>
Terminal window
npm install @lift-html/solid
import { liftSolid } from "@lift-html/solid";
import { createEffect, createSignal } from "solid-js";
const MyButton = liftSolid("my-button", {
init() {
const button = this.querySelector("button");
if (!button) throw new Error("<my-button> must contain a <button>");
const [count, setCount] = createSignal(0);
button.onclick = () => setCount(count() + 1);
createEffect(() => {
button.textContent = `Clicks: ${count()}`;
});
},
});
  • 🚀 Tiny - Start with 150 bytes, scale up as needed
  • 🔧 Framework-agnostic - Works with any existing setup
  • 🎯 Type-safe - Full TypeScript support
  • 🌐 Web standards - Uses native web APIs and standards
  • ♻️ Reactive - Optional SolidJS integration for reactive state
  • 🔥 HMR support - Hot Module Replacement out of the box

Traditional web component frameworks often focus on rendering HTML on the client side. Lift HTML takes a different approach:

  • Server-rendered HTML - Your HTML is generated on the server, providing better SEO and initial load performance
  • Progressive enhancement - Components add interactivity to existing HTML rather than replacing it
  • Better accessibility - HTML is available immediately, even before JavaScript loads
  • Simpler mental model - You write HTML, then enhance it with JavaScript

Here’s a complete example that works right now:

<my-button>
<button disabled>
Loading...
</button>
</my-button>
<script type="module">
import { liftSolid } from "https://esm.sh/@lift-html/solid";
import { createEffect, createSignal } from "https://esm.sh/solid-js";
const MyButton = liftSolid("my-button", {
init() {
const button = this.querySelector("button");
if (!button) throw new Error("<my-button> must contain a <button>");
button.disabled = false;
const [count, setCount] = createSignal(0);
button.onclick = () => setCount(count() + 1);
createEffect(() => {
button.textContent = `Clicks: ${count()}`;
});
},
});
</script>

Total code size: 3.41kb gzip