Skip to content

Instantly share code, notes, and snippets.

@hnordt
Last active November 12, 2025 22:29
Show Gist options
  • Select an option

  • Save hnordt/fd5ec2d89bd73873a9b8d0f4ced6609a to your computer and use it in GitHub Desktop.

Select an option

Save hnordt/fd5ec2d89bd73873a9b8d0f4ced6609a to your computer and use it in GitHub Desktop.
{
"compilerOptions": {
"lib": ["dom", "deno.ns"],
"jsx": "precompile",
"jsxImportSource": "preact"
},
"tasks": {
"dev": "deno run --allow-net --watch server.tsx",
},
"imports": {
"linkedom": "npm:linkedom@^0.18.12",
"preact-render-to-string": "npm:preact-render-to-string@^6.6.3",
"preact": "npm:preact@^10.27.2",
}
}
// We need this to convert our JSX into HTML at runtime.
import { renderToStaticMarkup } from "preact-render-to-string";
// Needed because HTMLElement isn’t available in Deno yet.
import { HTMLElement } from "linkedom";
// Enables type-safe usage of custom HTML elements in JSX.
declare global {
namespace preact.JSX {
interface IntrinsicElements {
"el-hello": { url: string };
}
}
}
// Defined on the server for type-safety, but only sent as a script tag.
// Per spec, Function.prototype.toString() returns source code, we use that here.
class HelloElement extends HTMLElement {
// Tell the DOM to watch these props so they can be changed via JS or devtools.
static observedAttributes = ["url"];
// Type-safe helper for convenience.
get props() {
return this.getAttributeNames().reduce((acc, name) => ({
...acc,
[name]: this.getAttribute(name),
}), {}) as preact.JSX.IntrinsicElements["el-hello"];
}
// Same as componentDidMount.
connectedCallback() {
this.render();
}
// Same as componentDidUpdate.
attributeChangedCallback() {
this.render();
}
render() {
// For simplicity, I’m just rendering a string here, but we could add
// preact-render-to-string to the client and use it instead.
this.innerHTML = `<div>Hello from ${new URL(this.props.url).pathname}!</div>`;
}
}
function App(props: { url: string }) {
return (
<html>
<head>
<style
dangerouslySetInnerHTML={{
__html: `body { background-color: #fff; }`,
}}
/>
<script
type="module"
dangerouslySetInnerHTML={{
__html: `customElements.define("el-hello", ${HelloElement});`,
}}
/>
</head>
<body>
<el-hello url={props.url} />
</body>
</html>
);
}
Deno.serve((req) =>
new Response(renderToStaticMarkup(<App url={req.url} />), {
headers: {
"Content-Type": "text/html; charset=utf-8",
},
})
);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment