isum
v1.1.0
Published
isomorphic uhtml
Readme
isum
isomorphic µhtml
Tiny wrapper that imports uhtml in the browser and uhtml/ssr in Node.js/Bun
for quick & easy client and server-side rendering (SSR/SSG). With a few extra
niceties.
All credits to Andrea Giammarchi for creating this mighty lib.
Example project using isum: ANSI.tools
The Main Idea™
- Plain and web standards-based JS library, i.e. not a (meta) framework
- Use
renderandhtmlto render template literals - Use fine-grained reactivity with
isum/reactiveand @preact/signals-core
Use the provided document of isum and Vite (or something else that
builds/bundles) and get SSG for free.
App
This runs in both browsers and runtimes like Node.js:
import { document, html, render } from 'isum';
export class App {
constructor() {
this.render();
}
handleClick(event) {
console.log(event);
}
render() {
const view = html`<button @click=${this.handleClick}>Hello!</button>`;
render(document.getElementById('app'), view);
}
}Please refer to the µhtml docs for details.
Reactivity with Signals
Introduced in v1.1.0
Import the same from isum/reactive and get @preact/signals-core's
fine-grained reactivity for free:
import { document, reactive } from 'isum/preactive';
const count = signal(0);
export function renderApp() {
const render = reactive(effect); // must not call at root module level
render(
document.body,
() =>
// second argument must be a function
html`<button onclick=${() => count.value++}>
Clicks: ${count.value}
</button>`
);
}This will render the initial values during SSG.
Please refer to the the µhtml docs for details.
Client-side
Bootstrap in index.js:
import { App } from './app.ts';
new App();Browser
Initial template/container index.html:
<!doctype html>
<html>
<head>
<link rel="stylesheet" href="styles.css" />
</head>
<body>
<main id="app"></main>
<script type="module" src="index.js"></script>
</body>
</html>SSG
Read template, render application, write result:
import { readFileSync, writeFileSync } from 'node:fs';
import initSSR from 'isum'; // must import same as in rest of app (e.g. 'isum/preactive')
import { renderApp } from './app.js';
const pages = {
'index.html': () => renderApp()
};
for (const [filePath, render] of Object.entries(pages)) {
const template = readFileSync(filePath, 'utf-8');
const { document } = init(template);
render();
writeFileSync(filePath, document.toString());
}This renders the <button> inside <main>.
This should scale well due to ESM live bindings. Here's an example build script.
Look ma, no bundler!
Run the app in the browser without a build step:
<!doctype html>
<html>
<head>
<link rel="stylesheet" href="styles.css" />
<script type="importmap">
{
"imports": {
"isum": "https://unpkg.com/[email protected]/browser.js",
"udomdiff": "https://unpkg.com/[email protected]/min.js",
"uhtml": "https://unpkg.com/[email protected]/keyed.js"
}
}
</script>
</head>
<body>
<main id="app"></main>
<script type="module" src="index.js"></script>
</body>
</html>CSS imports?
Ignore any CSS imports in JS modules:
node --import isum/no-css build.jsUseful when using e.g. Vite. isum pairs great with Vite.
