Typlet: Typst Math on web (Avaliable on npm and TypstPad)

Hi everyone! I’ve been building Typlet, a small JavaScript library that renders Typst math in the browser and in Node, the way KaTeX does it for LaTeX: one script plus web fonts, a synchronous render() call, and no WASM or compiler. Version 0.1.0 is on npm.

Laid out the way Typst lays it out

Typlet doesn’t hand formulas to the browser’s MathML engine. Its parser, evaluator and math layout are ports of Typst’s own code: 70 of its source files come from typst-syntax, typst-eval, typst-library, typst-layout and typst-html. It draws the result as HTML in New Computer Modern Math, the font Typst uses, and puts MathML beside it, hidden for screen readers.

It’s tested against the real Typst compiler:

  • Of the 1,823 test formulas that Typst renders, Typlet lays out 1,658, and the frames match Typst’s to a thousandth of a point (all but three, which differ on purpose). It draws 158 as MathML with a warning and refuses 7.
  • Its HTML is compared with Typst’s SVG, pixel by pixel, in Chromium, Firefox and WebKit.
  • Fuzzers compare its parser and evaluator with Typst’s on random formulas.

Embedded code works

Typlet evaluates the code formulas use: #let values and functions, set rules, content blocks, text, box, cancel, equation numbering and more. A preamble option does what KaTeX’s macros do:

import { renderToString } from 'typlet';

renderToString('norm(u + v) <= norm(u) + norm(v)', {
  displayMode: true,
  preamble: '#let norm(x) = $lr(|| #x ||)$\n#set math.equation(numbering: "(1)")',
  equationCounter: { value: 0 },
});

What needs Typst’s document engine (show rules, loops, context, counters, file access) is refused with a clear error. If your page needs those too, the fallback option lets it render them another way.

Using it

npm install typlet
import { render } from 'typlet';

render('sum_(i=1)^n i = (n(n+1))/2', element, { displayMode: true });
Or from a CDN
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/typlet@0.1.0/fonts/typlet.css">
<script src="https://cdn.jsdelivr.net/npm/typlet@0.1.0/dist/typlet.min.js"></script>
<script>
  typlet.render('sum_(i=1)^n i = (n(n+1))/2', document.getElementById('formula'), { displayMode: true });
</script>

It also comes with:

  • auto-render, which finds the formulas in a page, with Typst’s own syntax: $x$ inline, $ x $ display;
  • copy as Typst, so copied formulas paste as their source;
  • a <typlet-math> element and a typlet command;
  • plugins for rehype (rehype-typlet) and markdown-it (markdown-it-typlet).

Size and speed

A page of 74 formulas downloads
Typlet 118 KB of script, plus about 210 KB of fonts, fetched as characters need them
KaTeX 62 KB of script, plus about 200 KB of fonts

Scripts are measured brotli-compressed. Once its code is warm, Typlet renders a formula about as fast as KaTeX. On a page’s first load it’s slower: its first formula takes 1.2 to 1.5 times as long as KaTeX’s, and a page of 73 formulas about twice as long. The documentation site has a comparison page that measures both in your browser.

Limitations in 0.1
  • Formulas with a symbol that New Computer Modern Math lacks, text in Cyrillic, Hebrew, Arabic, Devanagari or Latin letters beyond Western European ones, raw text, or text in another font are drawn as MathML by the browser, with a warning.
  • show rules, loops, context, counters and file access are refused.
  • New Computer Modern Math is the only math font.
  • It reproduces Typst 0.15.1.

Try it

Feedback welcome

The most useful thing you can send me is a formula that Typlet draws differently from Typst. Typlet aims to match Typst exactly, so any difference is a bug: please open an issue with the formula. I’d also love to hear where you’d use it: blogs, docs, chat, editors.

Typlet is MIT-licensed and independent; it isn’t affiliated with Typst GmbH. Thanks to the Typst team, whose code most of Typlet is ported from, to Antonis Tsolomitis for New Computer Modern, and to mmtypst, whose way of testing with typst eval Typlet borrowed.

12 Likes

v0.1.1 is out!

Release v0.1.1 · dbccccccc/Typlet

typlet - npm

  1. Support equation show-set rules, including .where(block: …) selectors and Typst’s styling precedence.
  2. Render extended Latin text, ligature characters, and Arabic mathematical letters as HTML. Rebuild fonts and metrics; the browser bundle grows by about 3 KB compressed.
  3. Add CLI options --max-collection-size and --max-string-length.
  4. Make warning comparisons independent of order and improve browser tests for font metric rounding.
  5. Add regression cases, correct benchmark and corpus counts, and remove the external TypstPad corpus.
  6. Expand CI to Windows, macOS, and Node 26; add deeper fuzzing, generated-file checks, release-version checks, and failure artifacts.
  7. Automate npm publishing through OIDC, with provenance, release rehearsals, retries, and documentation deployment after publishing.
  8. Improve tool reproducibility with locked Rust dependencies and pinned Python packages. Restrict preview servers to localhost and tighten file access.
  9. Update development dependencies, add weekday dependency checks and Typst release alerts, refresh documentation, and bump all three packages to 0.1.1.
1 Like

Hi everyone! Typlet 0.2.0 was just released.

What’s new in 0.2.0:

  • CJK in HTML layout: Chinese, Japanese, and Korean characters inside math no longer force the whole equation to fall back to MathML.
  • renderToResult() API: Returns { markup, drawnAs, warnings, number } with typed warning kinds (fallback, approximation, deprecation, other).
  • typlet --batch CLI: Stream JSON Lines over stdin/stdout to render thousands of equations from Python/Rust/Go static site generators in a single process.
  • contrib/typlet.typ: Write equations once in .typ—renders native math in PDF and <typlet-math> in typst compile --features html.
  • Hardened & faster: Protected all eval/layout paths against stack overflows and DAG bombs, linear-time typ raw block highlighting, and faster tall delimiter assembly.
  • Plugin fixes & recipes: Native Hexo (require()) support and per-doc numbering reset in markdown-it-typlet, two-phase replacement in rehype-typlet, plus VitePress/Docusaurus/Hexo guides.

GitHub: Release v0.2.0 · dbccccccc/Typlet · GitHub

1 Like