This package is under development and will be frequently updated. The author would appreciate any help, advice, and pull requests! Thank you for your understanding 😊
Morph is an embeddable fullstack library for building Hypermedia-Driven Applications without a build step, based on HTMX.
Morph combines the best of SSR, SPA, and islands architecture, while sticking to plain HTML, CSS, and JS.
I created Morph while optimizing the development of Telegram Web Apps using Deno and Deno Deploy. Traditional stacks that separate frontend and backend with complex APIs and use React or Vue felt overly heavy, complex, and expensive for small projects.
Currently, Morph runs on Hono, but support for other backends may be added in the future.
- Each component can call its own API that returns hypertext (other components)\
- All components are rendered on the server and have access to server-side context\
- Components can be rendered and re-rendered independently\
- Components form a hierarchy, can be nested in one another, and returned from APIs\
- Minimal or no client-side JavaScript\
- No build step\
- No need to design API data structures upfront\
- The library can be embedded into any Deno/Node/Bun project\
Morph is ideal when there’s no need to split frontend and backend into separate services. It works especially well for small Telegram bots, desktop apps, or internal tools that don’t justify a full frontend stack but still need a clean and dynamic UI.
Denodeno add jsr:@vseplet/morph jsr:@hono/hono
Bunbunx jsr add @vseplet/morph
bun add hono
Nodenpx jsr add @vseplet/morph
npm i --save hono @hono/node-server
Deno
import { Hono } from "@hono/hono";
import { component, fn, html, meta, morph, styled } from "@vseplet/morph";
Bun
import { Hono } from "hono";
import { component, fn, html, meta, morph, styled } from "@vseplet/morph";
Node
import { serve } from '@hono/node-server'
import { Hono } from "hono";
import { component, fn, html, meta, morph, styled } from "@vseplet/morph";
const app = new Hono()
.all("/*", async (c) =>
await morph
.page("/", component(async () => html`
${meta({ title: "Hello, World!" })}
<h1>Hello, World!</h1>
<pre class="${styled`color:red;`}">${
(await (await fetch("https://icanhazdadjoke.com/", {
headers: {
Accept: "application/json",
"User-Agent": "My Fun App (https://example.com)",
},
})).json()).joke
}</pre>
${fn(() => alert("Hello!"))}
`),
)
.fetch(c.req.raw));
Deno
Bun
Node
Denodeno -A main.ts
Bunbun main.ts
Nodenode --experimental-strip-types main.ts