KitaJS HTML
is a 0 dependencies fast and concise HTML generator for JavaScript with JSX syntax.
Table of Contents
Installing
npm install @kitajs/html
Getting Started
Install @kitajs/html
with your favorite package manager, import it into the top of your jsx
/tsx
file and change your tsconfig.json to transpile jsx syntax.
// tsconfig.json
{
"compilerOptions": {
"jsx": "react",
"reactNamespace": "html"
}
}
import html from '@kitajs/html'
console.log(<div>Hello World</div>)
function route(request, response) {
return response
.header('Content-Type', 'text/html')
.send(<div>Hello World</div>)
}
fs.writeFileSync(
'index.html',
<html>
<head>
<title>Hello World</title>
</head>
<body>
<div>Hello World</div>
</body>
</html>
)
function Layout({ name, children }: html.PropsWithChildren<{ name: string }>) {
return (
<html>
<head>
<title>Hello World</title>
</head>
<body>
<div>Hello {name}</div>
{children}
</body>
</html>
)
}
console.log(<Layout name="World">I'm in the body!</Layout>)
typeof (<div>Hello World</div>) === 'string'
This package just provides functions to transpile JSX to a HTML string, you can imagine doing something like this before, but now with type checking and intellisense:
const html = `<div> Hello World!<div>` ❌
const html = (<div>Hello World!<div>) ✅
Sanitization
This package is a HTML builder, not an HTML sanitizer. This means that it does not sanitize any input, and you should sanitize where its needed. However, we escape all attribute values to avoid breaking out of the html attribute/tag.
const script = '<script>alert("hacked!")</script>'
const html = (
<>
<div style={'"&<>\''}></div>
<div>{script}</div>
</>
)
Will result into this html below but minified:
<div style=""&<>'"></div>
<div>
<script>
alert('hacked!')
</script>
</div>
Compiling html
When you have static html, is simple to get amazing performances, just save it to a constant and reuse it. However, if you need to hydrate the html with dynamic values in a super fast way, you can use the compile
property to compile the html and reuse it later.
import html from '@kitajs/html'
const compiled = html.compile<['param1', 'param2']>(
<div>
<div>$param1</div>
<div>$param2</div>
<div>$notFound</div>
</div>
)
const html = compiled({ param1: 'Hello', param2: 'World!' })
This makes the html generation almost 3000x faster than just using jsx normally.
Variables that were not passed to the compile
function are ignored silently, this way you can reuse the result into another compile
function or just because the your "$val
" was supposed to be a static value.
Fragments
JSX does not allow multiple root elements, but you can use a fragment to group multiple elements:
const html = (
<>
<div>1</div>
<div>2</div>
</>
)
Learn more about JSX syntax here!
Supported HTML
All HTML elements and attributes are supported, except for the svg.
Missing an element or attribute? Please create an issue or a PR to add it. It's easy to add.
Kebab case
HTML tags and attributes should be case insensitive, however JSX syntax does not allow <kebab-case>
elements. Therefore we transform all <camelCase>
tags into <camel-case>
to allow usage of custom html tags.
This transformation also works for custom attributes you define on a custom element yourself. For example:
<kebabCase kebabCase="value"></kebabCase>
Becomes
<kebab-case kebab-case="value"></kebab-case>
Note, if you are using TypeScript
, you will have to extend JSX
namespace to allow it:
interface MathPower {
myExponential: number
children: number
}
declare namespace JSX {
interface IntrinsicElements {
mathPower: MathPower
}
}
const element = <mathPower myExponential={2}>{3}</mathPower>
Performance
This package is just a string builder on steroids, as you can see how this works. However we are running a benchmark with an JSX HTML with about 10K characters to see how it performs.
You can run this yourself by running pnpm bench
.
@kitajs/html:
13 604 ops/s, ±1.12% | 99.97% slower
@kitajs/html - compiled:
38 938 712 ops/s, ±2.49% | fastest
typed-html:
10 057 ops/s, ±1.78% | slowest, 99.97% slower
How it works
This can be easily done by using TypeScript's JSX support and changing the default react
bindings to our own html
namespace.
<ol start={2}>
{[1, 2].map((i) => (
<li>{i}</li>
))}
</ol>
Is transpiled by typescript compiler at build step to:
html.createElement(
'ol',
{ start: 2 },
[1, 2].map((i) => html.createElement('li', null, i))
)
Which results into this string:
<ol start="2">
<li>1</li>
<li>2</li>
</ol>