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

@isomtop/tyhtml-astro

v0.1.3

Published

Astro Content Layer loader that compiles Typst .typ files to HTML at build time via @isomtop/tyhtml. Each entry exposes the compiled HTML as body and the <meta> label as JSON data. Compatible with Astro 6 and 7.

Readme

tyhtml-astro

Astro Content Layer loader for @isomtop/tyhtml — compile .typ files to HTML at build time.

When to use this

tyhtml-astro is a thin wrapper. If any of the following are deal-breakers for you, see Alternatives below:

  • You need SVG output (e.g., for paged documents, math figures)
  • You want component-level rendering (<Typst code={...} /> inline in .astro files)
  • You're on Astro 3 or 4 (this package uses the Content Layer API introduced in Astro 5)

Otherwise, this package is the simplest path: ~100 lines, native Rust engine via @isomtop/tyhtml, Content Layer loader.

Installation

npm install @isomtop/tyhtml-astro @isomtop/tyhtml

Both @isomtop/tyhtml-astro and @isomtop/tyhtml are required. This package expects @isomtop/tyhtml ^0.1.4. astro should already be installed.

Usage

// src/content.config.ts
import { defineCollection, z } from 'astro:content'
import { typstLoader } from '@isomtop/tyhtml-astro'

const typ = defineCollection({
    loader: typstLoader({
        base: './src/content/typ',
        compile: {
            pretty: true,
            bodyOnly: true,
            metadataLabel: 'meta',
        },
    }),
    schema: z.object({
        title: z.string(),
        pubDate: z.coerce.date(),
        tags: z.array(z.string()).default([]),
        draft: z.boolean().default(false),
    }),
})

export const collections = { typ }
---
// src/pages/typ/[...slug].astro
import { getCollection } from 'astro:content'

export async function getStaticPaths() {
    const posts = await getCollection('typ', ({ data }) => !data.draft)
    return posts.map(post => ({
        params: { slug: post.id },
        props: post,
    }))
}

const { post } = Astro.props
---
<article>
    <h1>{post.data.title}</h1>
    <time>{post.data.pubDate.toISOString().slice(0, 10)}</time>
    <Fragment set:html={post.body} />
</article>

Typst fixture convention

Each .typ file should have a <meta> label with the frontmatter fields declared in your collection schema:

#let meta = (
  title: "Hello from Typst",
  pubDate: "2026-06-28",
  tags: ("intro", "tyhtml"),
  draft: false,
)

#metadata(meta) <meta>

= First heading

Body content here.

Options

typstLoader(options)

| Field | Type | Default | Description | |---|---|---|---| | base | string | './' | Directory to scan, relative to project root. | | engine.fontPaths | string[] | [] | Font directories scanned once at engine construction and merged with system fonts. Prefer this over compile.fontPaths for dirs that don't change per file. | | compile.pretty | boolean | true | Pretty-print HTML output. | | compile.bodyOnly | boolean | true | Strip <!DOCTYPE>/<html>/<body> wrapper. Set false to get full HTML. | | compile.noMetadata | boolean | false | Skip <meta> query (faster). | | compile.metadataLabel | string | 'meta' | Label to query for metadata. | | compile.fontPaths | string[] | [] | Per-call font directories. Layered on top of engine.fontPaths for each compile. |

How it works

The loader builds a single new TyHtml(options) engine per loader instance. The constructor is the explicit cold start: it scans engine.fontPaths once and merges them with the system font set, then caches the resulting Typst Library + font entries for the lifetime of the loader.

src/content/typ/post.typ
       ↓ (recursively found)
       ↓
   ┌─────────────────────────────────────┐
   │  TyHtml engine (one per loader)     │
   │  cold start: system fonts +         │
   │  engine.fontPaths scan              │
   └─────────────────────────────────────┘
       ↓ initial load: engine.compile (worker thread)
       ↓ dev watch event: engine.compileSync (inline)
┌──────────────────────────────────────┐
│  Astro Content Layer entry          │
│  id:    "post"                      │
│  data:  { title, pubDate, ... }     │ ← from <meta> label, JSON.parsed
│  body:  "<h1>...</h1>..."           │ ← compiled HTML
└──────────────────────────────────────┘
       ↓
set:html={post.body} renders the HTML
post.data.title etc. exposes frontmatter

compile runs the blocking work on a worker thread (used during the initial load), and compileSync runs inline (used by the dev-mode watch handler, where Vite needs the result before the next module evaluation). Per-call work is dominated by typst::compile itself — the engine state built at construction time is reused. Typst diagnostics with warning severity are logged unless silent: true is set; compile errors are always logged.

Alternatives

astro-typst by OverflowCat

A more feature-complete integration built on typst.ts (WASM). Choose this if you need any of:

  • SVG output — vector output for paged documents, math figures, charts
  • Component rendering<Typst code={source} /> inline in .astro files
  • JS ↔ Typst data passing — pass values from Astro to typst and query typst values back as typed AST
  • Internal cross-references<Jump.astro> snippet for in-document navigation
  • Astro 3 / 4 compatibility — uses the legacy addContentEntryType API

Trade-offs vs. tyhtml-astro:

  • Ships compiled JS in dist/ (we ship raw TS, compiled on consumer side via Vite)
  • Pulls 9 extra dependencies (we have 2 peerDeps: @isomtop/tyhtml + astro)
  • WASM startup cost (~50–200ms first compile) vs. native .node (<10ms)
  • No standalone TyHtml instance outside Astro (we expose it via the @isomtop/tyhtml peerDep — you can use new TyHtml() in scripts, build tools, etc.)

Direct pandoc

If you only need one-shot .typ → HTML conversion (e.g., a docs build script), pandoc 3.1.5+ has a built-in Typst reader/writer. No Astro integration needed.

License

MIT — see LICENSE.