HT.JS html-in-javascript, made simple, done right. Static Site Generator import { writeFile } from "node:fs/promises";
import { html, head, body, meta, title, link, h1 } from "html-in-javascript";
const page =
html({ lang: "en" },
head(
meta({ charset: "UTF-8" }),
meta({ name: "viewport", content: "width=device-width, initial-scale=1.0" }),
title("Welcome!"),
link({ rel: "stylesheet", href: "/css/style.css" }),
),
body({ class: "home-page" },
h1("Hello World!"),
)
)
await writeFile("public/index.html", page);
Server Side Rendered import express from 'express';
import { html, head, body, meta, title, link, h1 } from "html-in-javascript";
const page =
html({ lang: "en" },
head(
meta({ charset: "UTF-8" }),
meta({ name: "viewport", content: "width=device-width, initial-scale=1.0" }),
title("Welcome!"),
link({ rel: "stylesheet", href: "/css/style.css" }),
),
body({ class: "home-page" },
h1("Hello World!"),
)
)
const app = express();
app.get('/', (req, res) => {
res.send(page);
})
app.listen(3000, () => console.log("http://localhost:3000"));
Bundled for Browser // bundle.js
import * as esbuild from 'esbuild'
await esbuild.build({
entryPoints: ['src/js/foo.js', 'src/js/bar.js', 'src/js/buff.js'],
bundle: true,
minify: true,
sourcemap: true,
splitting: true,
format: "esm",
target: "esnext",
outdir: 'dist/js'
})
// foo.js
import { fragment, head, body, meta, title, link, h1 } from "html-in-javascript";
const page =
fragment(
head(
meta({ charset: "UTF-8" }),
meta({ name: "viewport", content: "width=device-width, initial-scale=1.0" }),
title("Welcome!"),
link({ rel: "stylesheet", href: "/css/style.css" }),
),
body({ class: "home-page" },
h1("Hello World!"),
)
)
document.documentElement.innerHTML = page;
<!-- index.html -->
<html lang="en">
<script src="/js/bundle.js" type="module"></script>
</html>
Directly in Browser // foo.js
import { fragment, head, body, meta, title, link, h1 } from "https://cdn.jsdelivr.net/npm/html-in-javascript/esm.js";
const page =
fragment(
head(
meta({ charset: "UTF-8" }),
meta({ name: "viewport", content: "width=device-width, initial-scale=1.0" }),
title("Welcome!"),
link({ rel: "stylesheet", href: "/css/style.css" }),
),
body({ class: "home-page" },
h1("Hello World!"),
)
)
document.documentElement.innerHTML = page;
<!-- index.html -->
<html lang="en">
<script src="/js/foo.js" type="module"></script>
</html>
Docs Code div({
// Objects are the element's attributes
// in a key:value relationship eg.
class: "class names",
// becomes: class="class names"
id: "some-id",
"data-test-id": 123,
onclick: "this.style.color = 'blue'",
style: "color:red; padding:10px; border:1px solid black"
},
h2("hello world!"),
p(
"All of these 'element functions' eg. h2(), p(), strong() etc. return strings of HTML",
br(),
em("and can be nested exactly like html")
),
{
// you can have more than one object for the attributes
"data-foo": "bar"
// and they will be combined, but probably better to
// just have one attribute object, and to put it first
}
)
HTML <div
class="class names"
id="some-id"
data-test-id="123"
onclick="this.style.color = 'blue'"
style="color:red; padding:10px; border:1px solid black"
data-foo="bar">
<h2>hello world!</h2>
<p>All of these 'element functions' eg. h2(), p(), strong() etc. return strings of HTML<br><em>and can be nested exactly like html</em></p>
</div>
Example hello world! All of these 'element functions' eg. h2(), p(), strong() etc. return strings of HTMLand can be nested exactly like html
fragment similar to React's <>{content}</>
Used when you can't, or don't want a container <div>
Code const titleAndStyle = fragment(
title("HT.JS"),
link({ rel:"stylesheet", href:"/css/style.css" })
)
head(
meta({ charset:"UTF-8" }),
meta({ name:"viewport", content:"width=device-width, initial-scale=1.0" }),
titleAndStyle
)
HTML <head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>HT.JS</title>
<link rel="stylesheet" href="/css/style.css">
</head>
Inline scripts event, import, then default It's 2024! javascript modules are very well supported, and you can import them directly in the browser.
So instead of including a script in the head and exposing a function globally, you can import the script directly in the element's onclick event.
Code // increaseTextSize.js
export default (el) => {
const computedFontSize = parseFloat(window.getComputedStyle(el).fontSize);
el.style.fontSize = (computedFontSize * 1.1) + "px"
}
// index.js
button({
onclick: "import('/js/increaseTextSize.js').then(M => M.default(this))"
}, "Increase Text Size")
Example Increase Text Size Extensible At it's core HT.JS is just a collection of functions, so you can easily add your own to create reusable components.
// pageHead.js
import { head, meta, link, title, fragment } from "html-in-javascript";
const deviceMeta = fragment(
meta({
charset: "UTF-8"
}),
meta({
name: "viewport",
content: "width=device-width, initial-scale=1.0"
})
)
const favicons = fragment(
link({
rel: "apple-touch-icon",
sizes: "180x180",
href: "/apple-touch-icon.png"
}),
link({
rel: "icon",
type: "image/png",
sizes: "32x32",
href: "/favicon-32x32.png"
}),
link({
rel: "icon",
type:"image/png",
sizes: "16x16",
href:"/favicon-16x16.png"
}),
link({
rel: "manifest",
href: "/site.webmanifest"
}),
link({
rel: "mask-icon",
href: "/safari-pinned-tab.svg",
color: "#5bbad5"
}),
meta({
name: "msapplication-TileColor",
content: "#2d89ef"
}),
meta({
name: "theme-color",
content: "#ffffff"
})
)
export default ({
pageTitle = "example.com",
description = "Placeholder description",
} = {},
...rest
) =>
head(
deviceMeta,
favicons,
title(pageTitle),
link({
rel: "stylesheet",
href: "/css/style.css"
}),
meta({
name: "description",
content: description
}),
...rest
)
// index.js
import { html, body, h1, p, script } from "html-in-javascript";
import pageHead from "./pageHead.js";
const page =
html({ lang: "en" },
pageHead({
pageTitle: "example.com | My Page",
description: "My page description"
},
script({ src:"/js/someScript.js" })
),
body(
h1("My Page"),
p("This is my page")
)
)
HTML <html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
<link rel="manifest" href="/site.webmanifest">
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#5bbad5">
<meta name="msapplication-TileColor" content="#2d89ef">
<meta name="theme-color" content="#ffffff">
<title>example.com | My Page</title>
<link rel="stylesheet" href="/css/style.css">
<meta name="description" content="My page description">
<script src="/js/someScript.js"></script>
</head>
<body>
<h1>My Page</h1>
<p>This is my page</p>
</body>
</html>