Skip to content

gurgunday/ghtml

Repository files navigation

ghtml img.shields.io/bundlephobia/minzip/ghtml

taghtml lets you replace your template engine with fast JavaScript by leveraging the power of tagged templates.

Works in the browser. No runtime dependencies. Faster than React & Co. ~3x faster than EJS and ~13x faster than common-tags.

ghtml.gif

Installation

npm i ghtml

Or import directly from a CDN:

import { html } from "https://cdn.jsdelivr.net/npm/ghtml/+esm";

API

html

The html function is designed to tag template literals and automatically escape their expressions. To intentionally bypass escaping a specific expression, prefix it with !.

htmlGenerator

The htmlGenerator function acts as the generator version of the html function. It facilitates the creation of HTML fragments iteratively, making it ideal for parsing large templates or constructing HTML content dynamically.

Note:

Keep in mind that, in Node.js, all else being equal, streaming a response using synchronous generators is always slower than processing everything directly and sending it at once — this also applies to TTFB. However, if a template includes promises that do asynchronous operations (I/O, etc.), then htmlAsyncGenerator can be used to stream the response as those promises get resolved, which does indeed improve TTFB.

htmlAsyncGenerator

This version of HTML generator should be preferred for asynchronous and streaming use cases. The output is generated as the promise expressions resolve or stream expressions send data.

Note:

Because they return generators instead of strings, a key difference of htmlGenerator and htmlAsyncGenerator is their ability to recognize and properly handle iterable elements within array expressions. This is to detect nested htmlGenerator and htmlAsyncGenerator usage, enabling scenarios such as ${[1, 2, 3].map(i => htmlGenerator`<li>${i}</li>`)}.

includeFile

Available in Node.js, the includeFile function is a wrapper around readFileSync. It reads and returns the content of a file while caching it in memory for faster future reuse.

Usage

html

import { html } from "ghtml"; const username = '<img src="https://example.com/pwned.png">'; const greeting = html`<h1>Hello, ${username}</h1>`; console.log(greeting); // Output: <h1>Hello, &#60;img src&#61;&#34;https://example.com/pwned.png&#34;&#62;</h1>

To bypass escaping:

const img = '<img src="https://example.com/safe.png">'; const container = html`<div>!${img}</div>`; console.log(container); // Output: <div><img src="https://example.com/safe.png"></div>

When nesting multiple html expressions, make sure to use ! as the inner calls do their own escaping:

const someCondition = Math.random() >= 0.5; const data = { username: "John", age: 21, }; const htmlString = html`  <div>  !${someCondition  ? html`  <p>Data:</p>  <ul>  !${Object.values(data).map(  ([key, val]) => html`<li>${key}: ${val}</li>`,  )}  </ul>  `  : html`<p>No data...</p>`}  </div> `;
import { html } from "ghtml"; import http from "node:http"; http .createServer((req, res) => { const htmlContent = html`<!doctype html>  <html>  <p>You are at: ${req.url}</p>  </html>`; res.writeHead(200, { "Content-Type": "text/html;charset=utf-8" }); res.write(htmlContent); res.end(); }) .listen(3000);

htmlGenerator

import { htmlGenerator as html } from "ghtml"; import { Readable } from "node:stream"; import http from "node:http"; const generator = function* () { yield "Hello, World!"; }; http .createServer((req, res) => { const htmlContent = html`<!doctype html>  <html>  <p>${generator()}</p>  </html>`; const readableStream = Readable.from(htmlContent); res.writeHead(200, { "Content-Type": "text/html;charset=utf-8" }); readableStream.pipe(res); }) .listen(3000);

htmlAsyncGenerator

import { htmlAsyncGenerator as html } from "ghtml"; import { createReadStream } from "node:fs"; import { readFile } from "node:fs/promises"; import { Readable } from "node:stream"; import http from "node:http"; const asyncGenerator = async function* () { const helloWorld = new Promise((resolve) => { setTimeout(() => { resolve("<br /><br />Hello, World!"); }, 2500); }); yield await readFile("./.gitignore", "utf8"); yield helloWorld; }; http .createServer((req, res) => { const htmlContent = html`<!doctype html>  <html>  <p>!${asyncGenerator()}</p>  <code>${readFile("./README.md", "utf8")}</code>  <code>${createReadStream("./README.md", "utf8")}</code>  </html>`; const readableStream = Readable.from(htmlContent); res.writeHead(200, { "Content-Type": "text/html;charset=utf-8" }); readableStream.pipe(res); }) .listen(3000);

includeFile

import { includeFile } from "ghtml/includeFile.js"; const logo = includeFile("static/logo.svg"); console.log(logo); // Output: content of "static/logo.svg"

Benchmarks

Latest results from Platformatic's SSR Showdown (fastify-html is ghtml):

ghtml-perf.png

Latest results from KitaJS HTML:

benchmark time (avg) (min … max) p75 p99 p999 --------------------------------------------------- ----------------------------- • Real World Scenario --------------------------------------------------- ----------------------------- KitaJS/Html 505 µs/iter (387 µs … 2'007 µs) 417 µs 1'209 µs 1'857 µs Typed Html 1'844 µs/iter (1'604 µs … 2'415 µs) 2'088 µs 2'211 µs 2'415 µs VHtml 2'424 µs/iter (2'250 µs … 2'864 µs) 2'462 µs 2'829 µs 2'864 µs React JSX 6'416 µs/iter (5'893 µs … 9'399 µs) 6'840 µs 9'399 µs 9'399 µs Preact 970 µs/iter (673 µs … 5'038 µs) 766 µs 2'224 µs 5'038 µs React 6'319 µs/iter (5'885 µs … 7'306 µs) 6'678 µs 7'306 µs 7'306 µs Common Tags 2'967 µs/iter (2'774 µs … 3'801 µs) 2'916 µs 3'794 µs 3'801 µs Ghtml 225 µs/iter (184 µs … 1'567 µs) 206 µs 1'066 µs 1'450 µs JSXTE 4'489 µs/iter (3'605 µs … 6'215 µs) 4'517 µs 6'062 µs 6'215 µs   summary for Real World Scenario  Ghtml  2.25x faster than KitaJS/Html  4.32x faster than Preact  8.21x faster than Typed Html  10.8x faster than VHtml  13.22x faster than Common Tags  20x faster than JSXTE  28.15x faster than React  28.58x faster than React JSX

Security

Like similar tools, ghtml does not prevent all kinds of XSS attacks. It is the responsibility of developers to sanitize user inputs. Some inherently insecure uses include dynamically generating JavaScript, failing to quote HTML attribute values, and relying on unsanitized user-provided URIs.

About

Replace your template engine with fast JavaScript by leveraging the power of tagged templates

Resources

License

Security policy

Stars

Watchers

Forks

Packages