npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

faster-latex

v0.1.0

Published

Render full LaTeX documents to HTML (with MathML math) in the browser — pure Rust compiled to WebAssembly.

Readme

faster-latex

Render full LaTeX documents to HTML — sections, text, environments, lists, tables, cross-references, and math — in pure Rust, compiled to WebAssembly for the browser. Math is delegated to pulldown-latex, which emits MathML Core (rendered natively by every modern browser).

  • Infallible by contract. render_to_html never panics on any input; it returns HTML plus a list of non-fatal warnings.
  • Runtime macros. \newcommand/\newenvironment work in text and inside math — one thing pulldown-latex or LaTeX.js alone don't give you.
  • Small and dependency-light. The core crate's only runtime dependency is pulldown-latex (→ bumpalo). The WASM bundle is ~360 KB.
  • Dual-licensed MIT OR Apache-2.0, with no copyleft anywhere in the stack.
use faster_latex::{render_to_html, Options};

let out = render_to_html(
    r"\section{Hi} Euler: $e^{i\pi}+1=0$.",
    &Options::default(),
);
assert!(out.html.contains("<h2"));
assert!(out.html.contains("<math"));
for w in &out.warnings {
    eprintln!("{} (line {}): {}", w.kind.as_str(), w.line, w.message);
}

Pipeline

source ─▶ lex ─▶ expand macros ─▶ parse ─▶ number (pass 1) ─▶ emit HTML (pass 2)
                                                │
                                    math snippets ─▶ pulldown-latex ─▶ MathML

Math and verbatim are captured as opaque raw slices in the lexer, before the macro expander runs, so their contents can never be mangled. Macro expansion is driven by an explicit work stack (no recursion) with a fuel budget and depth limit, so macro bombs warn instead of hanging. Every user macro is also recorded in \def form and re-fed to pulldown-latex per math snippet, which is how a \newcommand defined once works in both text and $…$.

Supported subset (≈ LaTeX.js scope + runtime macros)

| Area | Supported | |------|-----------| | Structure | \documentclass, preamble, \title/\author/\date/\maketitle, \tableofcontents, \appendix | | Sectioning | \section\subparagraph (+ *), automatic numbering, \label/\ref/\eqref/\pageref/\nameref | | Text | \textbf \textit \emph \texttt \textsc \underline; declaration forms (\bfseries, …); font sizes \tiny\Huge; \\, paragraphs, comments | | Typography | ~, --/---, ``/'' quotes, accents → Unicode, many symbol commands (\LaTeX, \S, \ss, \ae, …), \verb | | Lists | itemize, enumerate, description (nested), \item[…] | | Blocks | quote, quotation, verse, center, flushleft, flushright, abstract, verbatim | | Floats | figure/table with \caption + numbering, \includegraphics (URL-sanitized) | | Tables | tabular with l/c/r columns and \hline | | Bibliography | thebibliography/\bibitem/\cite | | Math | $…$, \(…\), \[…\], $$…$$, equation(), align(), gather, multline, displaymath | | Macros | \newcommand/\renewcommand/\providecommand (9 args + optional default), \newenvironment |

Deliberately punted (warn, never crash)

Real package loading (\usepackage is ignored with a warning); \def, catcodes, conditionals, and other TeX primitives; TikZ / PGF; float placement and pagination; BibTeX processing; per-line equation numbering in align (rendered unnumbered with a warning). Unknown commands render as a visible <span class="fl-unknown"> fallback; unknown environments still render their contents. Anything unsupported produces a Warning, not a failure.

Security

Rendered HTML is safe to insert into a page:

  • All text is HTML-escaped; math text (\text{…}) is escaped by pulldown-latex.
  • \includegraphics URLs are allowlisted to relative paths and http/https; javascript:, data:, vbscript:, etc. are rejected with a warning and the image is omitted (the URL is never echoed into the output).
  • Locked in by the 952-xss-attempts.tex fixture and a panic-smoke test over mutated inputs.

Rust API

pub struct Options {
    pub base_url: Option<String>,
    pub predefined_macros: Vec<String>, // raw \newcommand… lines
    pub full_document: bool,            // full <html> shell vs. bare <article>
    pub max_expansions: u32,            // macro-bomb guard (default 100_000)
    pub max_nodes: u32,                 // node-count guard (default 500_000)
}
pub struct RenderOutput { pub html: String, pub warnings: Vec<Warning> }
pub fn render_to_html(source: &str, options: &Options) -> RenderOutput; // infallible
pub const ARTICLE_CSS: &str;  // shipped stylesheet, version-locked to the crate
pub const VERSION: &str;

Optional serde feature derives Serialize on Warning/WarningKind.

WebAssembly / JavaScript

Published to npm as faster-latex. The package ships two builds behind one name — a bundler build (the default import, for Vite/webpack/Rollup) and a web build (faster-latex/web, a plain ES module that fetches its own .wasm, for a CDN or <script type=module>). TypeScript types are included for both.

With a bundler (Vite, webpack, Rollup, Next.js…)

npm install faster-latex
// The bundler build auto-initializes the wasm on import — no init() call.
import { render, article_css, version } from "faster-latex";

const { html, warnings } = render(source, { fullDocument: false });
document.getElementById("out").innerHTML = html;

// Inject the shipped stylesheet once:
const style = document.createElement("style");
style.textContent = article_css();
document.head.appendChild(style);

From a CDN / no build step (browser, Deno)

Use the web subpath; call the default-exported init() once before rendering. The .wasm is resolved relative to the module URL, so no path config is needed:

<script type="module">
  import init, { render } from "https://cdn.jsdelivr.net/npm/faster-latex/web/faster_latex.js";
  await init();
  document.getElementById("out").innerHTML = render("$e^{i\\pi}+1=0$").html;
</script>

(unpkg works too: https://unpkg.com/faster-latex/web/faster_latex.js. Pin a version with [email protected] for production.)

render(source, options?) returns a plain object { html, warnings } (no .free()-able classes). The bundle uses the default allocator and console_error_panic_hook (default-on panic-hook feature).

Building the package locally

npm run build:npm        # -> dist/ (bundler/ + web/ + package.json)
npm run pack:npm         # build + npm pack, to inspect the tarball
npm run publish:npm      # build + npm publish ./dist --access public

The package version is taken from [workspace.package].version in Cargo.toml (one source of truth). CI publishes automatically when a vX.Y.Z tag matching that version is pushed — see .github/workflows/npm-publish.yml.

Stylesheet requirement

MathML renders without any CSS, but for correct spacing and fonts you must load the pulldown-latex math stylesheet and inject ARTICLE_CSS:

<link rel="stylesheet"
      href="https://cdn.jsdelivr.net/gh/carloskiki/[email protected]/styles.min.css">

The full_document output embeds ARTICLE_CSS and links this stylesheet for you.

Demo

A framework-free live editor lives in demo/:

wasm-pack build crates/faster-latex-wasm --target web --out-name faster_latex
cp -r crates/faster-latex-wasm/pkg demo/pkg
cd demo && python3 -m http.server 8000   # then open http://localhost:8000

Development

cargo test --workspace
cargo clippy --workspace --all-targets -- -D warnings
cargo fmt --check
wasm-pack build crates/faster-latex-wasm --target web   # pkg/*.wasm should be < 500 KB
wasm-pack test --headless --chrome crates/faster-latex-wasm

HTML snapshots (via insta) live in crates/faster-latex/tests/corpus/ — update them with cargo insta review.

Why a new parser?

As of mid-2026 there is no maintained, permissively licensed, pure-Rust full-document LaTeX parser. The strong parsers (texlab, RusTeX) are GPL; Tectonic is a C/XeTeX core with no WASM build; mitex's text mode is unfinished. So the document parser/emitter here is written from scratch (~3–5 kLoC) and only the math layer is reused, from the MIT-licensed pulldown-latex. See crates/faster-latex/tests/corpus/ATTRIBUTION.md for the MIT reference projects (LaTeX.js, unified-latex) used to shape the scope.

License

Licensed under either of

at your option. No GPL/AGPL/copyleft code is used anywhere in the dependency graph — the entire runtime stack is faster-latexpulldown-latexbumpalo, all MIT/Apache.

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual-licensed as above, without any additional terms or conditions.