A lightweight library for building server-rendered components with client-side enhancements.
enhanceable
provides a simple yet powerful way to create web applications that combine server-side rendering with progressive client-side enhancements. It uses a familiar HTML template syntax and a hooks-based API to bridge the gap between server and client behavior.
- 🚀 Server-side rendering with hydration capabilities
- 🧩 Component-based architecture for reusable UI elements
- 🔄 Seamless client-server bridge with the
use client
directive anduseEnhancements()
hook - 📝 HTML template literals for intuitive component authoring
- 🔌 Zero client-side JavaScript by default - add interactivity only where needed
npm install enhanceable
Components are simple functions that return HTML using the html
template literal:
import { html } from "enhanceable";
export function Wrapper({ children }) {
return html`
<div class="wrapper">
${children}
</div>
`;
}
- Create your component with
useEnhancements()
:
// counter.ts
import { html, useEnhancements } from "enhanceable";
import * as enhancements from "./counter.client.ts";
export function Counter({ initialCount = 0 }) {
const { handleIncrement } = useEnhancements(enhancements);
return html`
<button${{ onclick: handleIncrement }}>${initialCount}</button>
`;
}
- Define client-side behavior in a separate file:
// counter.client.ts
"use client";
export function handleIncrement(event: MouseEvent) {
if (!event.target || !(event.target instanceof HTMLElement)) {
throw new Error("invalid target");
}
event.target.textContent = "" + (Number(event.target.textContent) + 1);
}
import { html, render } from "enhanceable";
import { Counter } from "./components/counter.ts";
export default {
async fetch() {
return new Response(
await render(
() => html`
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Enhanceable App</title>
</head>
<body>
<h1>Counter Example</h1>
<${Counter} ${{ initialCount: 1 }} />
</body>
</html>
`
),
{
headers: {
"content-type": "text/html; charset=utf-8",
},
}
);
},
};
You can dynamically create and hydrate components on the client:
// In a client-side file
import { html } from "enhanceable";
import { hydrate } from "enhanceable/dom";
import { TodoItem } from "./todo.ts";
export async function addTodo(event: MouseEvent) {
const list = document.querySelector("ul");
const newItem = hydrate(
await html`<${TodoItem}>New Item</${TodoItem}>`
);
list.append(newItem);
}
The enhanceable/dom
module provides utilities for client-side DOM manipulation:
import { hydrate, next } from "enhanceable/dom";
// Find the next element matching a selector
const list = next(button, "ul");
// Hydrate a component for client-side use
const element = hydrate(htmlContent);
-
html
- Template literal tag for creating HTML content -
render(template)
- Renders a template to HTML string -
useEnhancements(module)
- Connects server components with client-side behavior
-
hydrate(html)
- Converts HTML string to DOM elements with enhancements -
next(element, selector)
- Finds the next element matching a selector