Repository files navigation

Typlete

Svelte 5 components and Markdown helpers for rendering Typst as SVG.

Requirements

  • Svelte >= 5.36
  • Svelte async rendering enabled: compilerOptions.experimental.async = true
  • Vite/SvelteKit-compatible asset handling for browser WASM imports
// svelte.config.jsexportdefault{compilerOptions: {experimental: {async: true}}};

Install

pnpm add typlete

Svelte usage

<scriptlang="ts">import { TypstInline, TypstBlock } from'typlete';</script>
<p>
Inline formula: <TypstInlinesource="integral_0^1 x^2 dif x" />
</p>
<TypstBlocksource="sum_(i=1)^n i = (n(n+1)) / 2" />

source is Typst math by default. Typlete wraps math input in Typst math delimiters and escapes delimiter-breaking characters ($ and #) before compiling. Do not include outer $...$; pass the formula body.

CSS color inheritance

The implicit foreground of inline SVG rendered by Typlete inherits the surrounding CSS color, just like normal text.

<pclass="secondary">
Energy: <TypstInlinesource="E = m c^2" />
</p>
<style>.secondary {color: var(--text-secondary);	}</style>

This also follows normal CSS state changes such as :hover, disabled states, theme classes, and inherited custom properties. No formula-specific color prop is required.

Math construction strokes inherit the same color too, including fraction bars, radical bars, overlines, and underlines.

Explicit Typst text colors still take precedence and are preserved in the generated SVG:

<TypstInlinesource="alpha + beta"preamble="#set text(fill: red)" />

Color inheritance applies when the SVG is embedded inline, including the Svelte components, the server helper when its SVG is inserted inline, and Markdown output: 'html'. SVG used as an external image (output: 'markdown-image', output: 'asset', or a normal <img>) cannot inherit color from the surrounding HTML document.

Raw Typst

Use inputMode="raw" for full Typst markup fragments that should not be wrapped as math. Raw mode passes the source to Typst unchanged, so user-controlled raw input needs separate policy around allowed Typst features and assets.

<TypstBlockinputMode="raw"source={'#rect(radius: 6pt, inset: 10pt)[#strong[Raw Typst]]'} />
<!-- math mode: Typlete wraps source in Typst math -->
<TypstInlinesource="alpha + beta" />
<!-- raw mode: Typlete passes source to Typst unchanged -->
<TypstInlineinputMode="raw"source={'$alpha + beta$'} />

inputMode="markup" is accepted as a compatibility alias for raw.

Props

Typst, TypstInline, and TypstBlock support these common props:

typeTypstMode='inline'|'block';typeTypstInputMode='math'|'raw'|'markup';typeTypstSvgSanitizer=(svg: string)=>string;
source?: string;
inputMode?: TypstInputMode;
preamble?: string;
textSize?: string;
pageMargin?: string;
cache?: boolean;
sanitize?: TypstSvgSanitizer;
ariaLabel?: string;
title?: string;
loadingLabel?: string;
throwOnError?: boolean;
errorMode?: 'badge'|'none';class?: string;

Typst also accepts mode. TypstInline and TypstBlock set mode automatically.

SSR and local runtime assets

During SSR/build, Typlete renders SVG on the server. After hydration, the browser keeps the server-rendered SVG and loads the Typst WASM runtime only when the formula has to be rendered again on the client.

Typlete ships its own copies of the compiler/renderer WASM modules and the default Typst text font set inside the package. Browser builds emit those files as application-local assets; SSR reads the same installed Typlete assets directly from disk and passes their bytes to the Typst runtime. Normal formula rendering therefore does not fetch runtime assets from a CDN, depend on transitive-package layout, or require consumers to copy WASM/fonts manually.

Raw Typst that explicitly imports external Typst packages can still perform network access according to the underlying Typst package registry. That is separate from Typlete's own runtime assets.

Error behavior

By default, render failures keep the last successful SVG visible and show a small error badge.

<TypstBlocksource={formula} errorMode="badge" />
<TypstBlocksource={formula} errorMode="none" />

Use throwOnError={true} when server-side render failures should fail the request/build instead of being converted to component state.

Server helper

import{renderTypstSvgServer}from'typlete/server';constsvg=awaitrenderTypstSvgServer({source: 'integral_0^1 x^2 dif x',mode: 'block',inputMode: 'math',preamble: '',textSize: '11pt',pageMargin: '0pt',cache: true});

Markdown blocks

Typlete only renders namespaced Typlete fences. Normal typst fences stay normal Markdown code blocks, so documentation can show Typst source without triggering rendering.

Raw Typst render block:

```typlete-typst#set text(size: 12pt)#rect[hello]```

Math render block. The content is treated as a formula body; delimiter-breaking $ and # characters are escaped before Typst compiles it:

```typlete-mathsum_(i=1)^n i = (n(n+1)) / 2```

Accepted render fence names:

typlete -> raw Typst render block
typlete raw -> raw Typst render block
typlete typst -> raw Typst render block
typlete-typst -> raw Typst render block
typlete-raw -> raw Typst render block
typlete math -> math render block
typlete-math -> math render block
typlete-typst-math -> math render block

Typst source code remains source code:

```typst#set text(size: 12pt)#rect[hello]```

For inline formulas in MDsveX, use the Svelte component directly. The same math escaping applies:

The value is <TypstInlinesource="alpha + beta" />.

Markdown transform

import{transformTypleteMarkdown}from'typlete/markdown';constoutput=awaittransformTypleteMarkdown(markdown,{output: 'component'});

Output modes:

typeTypleteMarkdownOutput='component'|'html'|'markdown-image'|'asset';

Component output

Best for MDsveX/Svelte-aware Markdown pipelines.

awaittransformTypleteMarkdown(markdown,{output: 'component'});

Produces Svelte component tags:

<TypstBlocksource={'#rect[hello]'} inputMode="raw" />
<TypstBlocksource={'alpha + beta'} />

HTML output

Best for renderers that accept raw HTML/SVG.

awaittransformTypleteMarkdown(markdown,{output: 'html'});

This renders SVG immediately and inserts it into the Markdown output.

Markdown image output

Best for renderers that do not support raw HTML but accept Markdown images.

awaittransformTypleteMarkdown(markdown,{output: 'markdown-image'});

This renders SVG and inserts it as a data:image/svg+xml Markdown image.

Asset output

Best for static sites.

awaittransformTypleteMarkdown(markdown,{output: 'asset',assetDir: 'static/typlete',assetBaseUrl: '/typlete'});

This writes SVG files to assetDir and inserts normal Markdown image links.

MDsveX preprocessor

Run the Typlete preprocessor before MDsveX so namespaced Typlete render fences are converted to Svelte component tags before MDsveX compiles the document.

// svelte.config.jsimportadapterfrom'@sveltejs/adapter-auto';import{mdsvex}from'mdsvex';import{createTypstMdsvexPreprocessor}from'typlete/markdown';constconfig={extensions: ['.svelte','.svx','.md'],preprocess: [createTypstMdsvexPreprocessor({output: 'component'}),mdsvex({extensions: ['.svx','.md']})],compilerOptions: {experimental: {async: true}},kit: {adapter: adapter()}};exportdefaultconfig;

For output: 'component', the preprocessor injects this import when a document contains a rendered Typst fence:

<scriptlang="ts">import { TypstInline, TypstBlock } from'typlete';</script>

Disable injection only if another MDsveX hook already imports the components:

createTypstMdsvexPreprocessor({output: 'component',injectComponentImports: false});

Rendering options for Markdown

Markdown helpers accept the same render options as component/server rendering:

awaittransformTypleteMarkdown(markdown,{output: 'asset',assetDir: 'static/typlete',assetBaseUrl: '/typlete',preamble: '',textSize: '11pt',pageMargin: '0pt',cache: true});

Sanitizing SVG

Typlete inserts compiler-produced SVG inline. By default it strips embedded SVG <script> tags and common script-like SVG attributes from that output. This is output-level defense-in-depth, not a sandbox for arbitrary raw Typst input or external assets. For stricter SVG sanitizing, pass a sanitizer.

DOMPurify is optional:

import{createDomPurifySvgSanitizer}from'typlete/sanitizers/dompurify';constsanitize=awaitcreateDomPurifySvgSanitizer();

Server-side DOMPurify requires both dompurify and jsdom:

import{createServerDomPurifySvgSanitizer}from'typlete/server/dompurify';constsanitize=awaitcreateServerDomPurifySvgSanitizer();

Limitations

  • Component SSR depends on Svelte experimental async rendering.
  • Plain typst fences are never render instructions; use typlete-typst, typlete-raw, or typlete-math when Markdown should render Typst.
  • Math mode and typlete-math escape $ and # before wrapping the source in Typst math delimiters. Raw mode and preamble are passed to Typst unchanged.
  • CSS text-color inheritance requires inline SVG. SVG loaded through <img>, including markdown-image and asset Markdown output, does not inherit the parent document's color.
  • Server rendering temporarily guards Typst runtime fetches so SvelteKit does not track external runtime fetches during SSR.
  • html, markdown-image, and asset output modes pre-render SVG immediately and are not reactive on the client.

About

Typst + Svelte

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Repository files navigation

Typlete

Svelte 5 components and Markdown helpers for rendering Typst as SVG.

Requirements

  • Svelte >= 5.36
  • Svelte async rendering enabled: compilerOptions.experimental.async = true
  • Vite/SvelteKit-compatible asset handling for browser WASM imports
// svelte.config.jsexportdefault{compilerOptions: {experimental: {async: true}}};

Install

pnpm add typlete

Svelte usage

<scriptlang="ts">import { TypstInline, TypstBlock } from'typlete';</script>
<p>
Inline formula: <TypstInlinesource="integral_0^1 x^2 dif x" />
</p>
<TypstBlocksource="sum_(i=1)^n i = (n(n+1)) / 2" />

source is Typst math by default. Typlete wraps math input in Typst math delimiters and escapes delimiter-breaking characters ($ and #) before compiling. Do not include outer $...$; pass the formula body.

CSS color inheritance

The implicit foreground of inline SVG rendered by Typlete inherits the surrounding CSS color, just like normal text.

<pclass="secondary">
Energy: <TypstInlinesource="E = m c^2" />
</p>
<style>.secondary {color: var(--text-secondary);	}</style>

This also follows normal CSS state changes such as :hover, disabled states, theme classes, and inherited custom properties. No formula-specific color prop is required.

Math construction strokes inherit the same color too, including fraction bars, radical bars, overlines, and underlines.

Explicit Typst text colors still take precedence and are preserved in the generated SVG:

<TypstInlinesource="alpha + beta"preamble="#set text(fill: red)" />

Color inheritance applies when the SVG is embedded inline, including the Svelte components, the server helper when its SVG is inserted inline, and Markdown output: 'html'. SVG used as an external image (output: 'markdown-image', output: 'asset', or a normal <img>) cannot inherit color from the surrounding HTML document.

Raw Typst

Use inputMode="raw" for full Typst markup fragments that should not be wrapped as math. Raw mode passes the source to Typst unchanged, so user-controlled raw input needs separate policy around allowed Typst features and assets.

<TypstBlockinputMode="raw"source={'#rect(radius: 6pt, inset: 10pt)[#strong[Raw Typst]]'} />
<!-- math mode: Typlete wraps source in Typst math -->
<TypstInlinesource="alpha + beta" />
<!-- raw mode: Typlete passes source to Typst unchanged -->
<TypstInlineinputMode="raw"source={'$alpha + beta$'} />

inputMode="markup" is accepted as a compatibility alias for raw.

Props

Typst, TypstInline, and TypstBlock support these common props:

typeTypstMode='inline'|'block';typeTypstInputMode='math'|'raw'|'markup';typeTypstSvgSanitizer=(svg: string)=>string;
source?: string;
inputMode?: TypstInputMode;
preamble?: string;
textSize?: string;
pageMargin?: string;
cache?: boolean;
sanitize?: TypstSvgSanitizer;
ariaLabel?: string;
title?: string;
loadingLabel?: string;
throwOnError?: boolean;
errorMode?: 'badge'|'none';class?: string;

Typst also accepts mode. TypstInline and TypstBlock set mode automatically.

SSR and local runtime assets

During SSR/build, Typlete renders SVG on the server. After hydration, the browser keeps the server-rendered SVG and loads the Typst WASM runtime only when the formula has to be rendered again on the client.

Typlete ships its own copies of the compiler/renderer WASM modules and the default Typst text font set inside the package. Browser builds emit those files as application-local assets; SSR reads the same installed Typlete assets directly from disk and passes their bytes to the Typst runtime. Normal formula rendering therefore does not fetch runtime assets from a CDN, depend on transitive-package layout, or require consumers to copy WASM/fonts manually.

Raw Typst that explicitly imports external Typst packages can still perform network access according to the underlying Typst package registry. That is separate from Typlete's own runtime assets.

Error behavior

By default, render failures keep the last successful SVG visible and show a small error badge.

<TypstBlocksource={formula} errorMode="badge" />
<TypstBlocksource={formula} errorMode="none" />

Use throwOnError={true} when server-side render failures should fail the request/build instead of being converted to component state.

Server helper

import{renderTypstSvgServer}from'typlete/server';constsvg=awaitrenderTypstSvgServer({source: 'integral_0^1 x^2 dif x',mode: 'block',inputMode: 'math',preamble: '',textSize: '11pt',pageMargin: '0pt',cache: true});

Markdown blocks

Typlete only renders namespaced Typlete fences. Normal typst fences stay normal Markdown code blocks, so documentation can show Typst source without triggering rendering.

Raw Typst render block:

```typlete-typst#set text(size: 12pt)#rect[hello]```

Math render block. The content is treated as a formula body; delimiter-breaking $ and # characters are escaped before Typst compiles it:

```typlete-mathsum_(i=1)^n i = (n(n+1)) / 2```

Accepted render fence names:

typlete -> raw Typst render block
typlete raw -> raw Typst render block
typlete typst -> raw Typst render block
typlete-typst -> raw Typst render block
typlete-raw -> raw Typst render block
typlete math -> math render block
typlete-math -> math render block
typlete-typst-math -> math render block

Typst source code remains source code:

```typst#set text(size: 12pt)#rect[hello]```

For inline formulas in MDsveX, use the Svelte component directly. The same math escaping applies:

The value is <TypstInlinesource="alpha + beta" />.

Markdown transform

import{transformTypleteMarkdown}from'typlete/markdown';constoutput=awaittransformTypleteMarkdown(markdown,{output: 'component'});

Output modes:

typeTypleteMarkdownOutput='component'|'html'|'markdown-image'|'asset';

Component output

Best for MDsveX/Svelte-aware Markdown pipelines.

awaittransformTypleteMarkdown(markdown,{output: 'component'});

Produces Svelte component tags:

<TypstBlocksource={'#rect[hello]'} inputMode="raw" />
<TypstBlocksource={'alpha + beta'} />

HTML output

Best for renderers that accept raw HTML/SVG.

awaittransformTypleteMarkdown(markdown,{output: 'html'});

This renders SVG immediately and inserts it into the Markdown output.

Markdown image output

Best for renderers that do not support raw HTML but accept Markdown images.

awaittransformTypleteMarkdown(markdown,{output: 'markdown-image'});

This renders SVG and inserts it as a data:image/svg+xml Markdown image.

Asset output

Best for static sites.

awaittransformTypleteMarkdown(markdown,{output: 'asset',assetDir: 'static/typlete',assetBaseUrl: '/typlete'});

This writes SVG files to assetDir and inserts normal Markdown image links.

MDsveX preprocessor

Run the Typlete preprocessor before MDsveX so namespaced Typlete render fences are converted to Svelte component tags before MDsveX compiles the document.

// svelte.config.jsimportadapterfrom'@sveltejs/adapter-auto';import{mdsvex}from'mdsvex';import{createTypstMdsvexPreprocessor}from'typlete/markdown';constconfig={extensions: ['.svelte','.svx','.md'],preprocess: [createTypstMdsvexPreprocessor({output: 'component'}),mdsvex({extensions: ['.svx','.md']})],compilerOptions: {experimental: {async: true}},kit: {adapter: adapter()}};exportdefaultconfig;

For output: 'component', the preprocessor injects this import when a document contains a rendered Typst fence:

<scriptlang="ts">import { TypstInline, TypstBlock } from'typlete';</script>

Disable injection only if another MDsveX hook already imports the components:

createTypstMdsvexPreprocessor({output: 'component',injectComponentImports: false});

Rendering options for Markdown

Markdown helpers accept the same render options as component/server rendering:

awaittransformTypleteMarkdown(markdown,{output: 'asset',assetDir: 'static/typlete',assetBaseUrl: '/typlete',preamble: '',textSize: '11pt',pageMargin: '0pt',cache: true});

Sanitizing SVG

Typlete inserts compiler-produced SVG inline. By default it strips embedded SVG <script> tags and common script-like SVG attributes from that output. This is output-level defense-in-depth, not a sandbox for arbitrary raw Typst input or external assets. For stricter SVG sanitizing, pass a sanitizer.

DOMPurify is optional:

import{createDomPurifySvgSanitizer}from'typlete/sanitizers/dompurify';constsanitize=awaitcreateDomPurifySvgSanitizer();

Server-side DOMPurify requires both dompurify and jsdom:

import{createServerDomPurifySvgSanitizer}from'typlete/server/dompurify';constsanitize=awaitcreateServerDomPurifySvgSanitizer();

Limitations

  • Component SSR depends on Svelte experimental async rendering.
  • Plain typst fences are never render instructions; use typlete-typst, typlete-raw, or typlete-math when Markdown should render Typst.
  • Math mode and typlete-math escape $ and # before wrapping the source in Typst math delimiters. Raw mode and preamble are passed to Typst unchanged.
  • CSS text-color inheritance requires inline SVG. SVG loaded through <img>, including markdown-image and asset Markdown output, does not inherit the parent document's color.
  • Server rendering temporarily guards Typst runtime fetches so SvelteKit does not track external runtime fetches during SSR.
  • html, markdown-image, and asset output modes pre-render SVG immediately and are not reactive on the client.

About

Typst + Svelte

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Typlete

Svelte 5 components and Markdown helpers for rendering Typst as SVG.

Requirements

  • Svelte >= 5.36
  • Svelte async rendering enabled: compilerOptions.experimental.async = true
  • Vite/SvelteKit-compatible asset handling for browser WASM imports
// svelte.config.jsexportdefault{compilerOptions: {experimental: {async: true}}};

Install

pnpm add typlete

Svelte usage

<scriptlang="ts">import { TypstInline, TypstBlock } from'typlete';</script>
<p>
Inline formula: <TypstInlinesource="integral_0^1 x^2 dif x" />
</p>
<TypstBlocksource="sum_(i=1)^n i = (n(n+1)) / 2" />

source is Typst math by default. Typlete wraps math input in Typst math delimiters and escapes delimiter-breaking characters ($ and #) before compiling. Do not include outer $...$; pass the formula body.

CSS color inheritance

The implicit foreground of inline SVG rendered by Typlete inherits the surrounding CSS color, just like normal text.

<pclass="secondary">
Energy: <TypstInlinesource="E = m c^2" />
</p>
<style>.secondary {color: var(--text-secondary);	}</style>

This also follows normal CSS state changes such as :hover, disabled states, theme classes, and inherited custom properties. No formula-specific color prop is required.

Math construction strokes inherit the same color too, including fraction bars, radical bars, overlines, and underlines.

Explicit Typst text colors still take precedence and are preserved in the generated SVG:

<TypstInlinesource="alpha + beta"preamble="#set text(fill: red)" />

Color inheritance applies when the SVG is embedded inline, including the Svelte components, the server helper when its SVG is inserted inline, and Markdown output: 'html'. SVG used as an external image (output: 'markdown-image', output: 'asset', or a normal <img>) cannot inherit color from the surrounding HTML document.

Raw Typst

Use inputMode="raw" for full Typst markup fragments that should not be wrapped as math. Raw mode passes the source to Typst unchanged, so user-controlled raw input needs separate policy around allowed Typst features and assets.

<TypstBlockinputMode="raw"source={'#rect(radius: 6pt, inset: 10pt)[#strong[Raw Typst]]'} />
<!-- math mode: Typlete wraps source in Typst math -->
<TypstInlinesource="alpha + beta" />
<!-- raw mode: Typlete passes source to Typst unchanged -->
<TypstInlineinputMode="raw"source={'$alpha + beta$'} />

inputMode="markup" is accepted as a compatibility alias for raw.

Props

Typst, TypstInline, and TypstBlock support these common props:

typeTypstMode='inline'|'block';typeTypstInputMode='math'|'raw'|'markup';typeTypstSvgSanitizer=(svg: string)=>string;
source?: string;
inputMode?: TypstInputMode;
preamble?: string;
textSize?: string;
pageMargin?: string;
cache?: boolean;
sanitize?: TypstSvgSanitizer;
ariaLabel?: string;
title?: string;
loadingLabel?: string;
throwOnError?: boolean;
errorMode?: 'badge'|'none';class?: string;

Typst also accepts mode. TypstInline and TypstBlock set mode automatically.

SSR and local runtime assets

During SSR/build, Typlete renders SVG on the server. After hydration, the browser keeps the server-rendered SVG and loads the Typst WASM runtime only when the formula has to be rendered again on the client.

Typlete ships its own copies of the compiler/renderer WASM modules and the default Typst text font set inside the package. Browser builds emit those files as application-local assets; SSR reads the same installed Typlete assets directly from disk and passes their bytes to the Typst runtime. Normal formula rendering therefore does not fetch runtime assets from a CDN, depend on transitive-package layout, or require consumers to copy WASM/fonts manually.

Raw Typst that explicitly imports external Typst packages can still perform network access according to the underlying Typst package registry. That is separate from Typlete's own runtime assets.

Error behavior

By default, render failures keep the last successful SVG visible and show a small error badge.

<TypstBlocksource={formula} errorMode="badge" />
<TypstBlocksource={formula} errorMode="none" />

Use throwOnError={true} when server-side render failures should fail the request/build instead of being converted to component state.

Server helper

import{renderTypstSvgServer}from'typlete/server';constsvg=awaitrenderTypstSvgServer({source: 'integral_0^1 x^2 dif x',mode: 'block',inputMode: 'math',preamble: '',textSize: '11pt',pageMargin: '0pt',cache: true});

Markdown blocks

Typlete only renders namespaced Typlete fences. Normal typst fences stay normal Markdown code blocks, so documentation can show Typst source without triggering rendering.

Raw Typst render block:

```typlete-typst#set text(size: 12pt)#rect[hello]```

Math render block. The content is treated as a formula body; delimiter-breaking $ and # characters are escaped before Typst compiles it:

```typlete-mathsum_(i=1)^n i = (n(n+1)) / 2```

Accepted render fence names:

typlete -> raw Typst render block
typlete raw -> raw Typst render block
typlete typst -> raw Typst render block
typlete-typst -> raw Typst render block
typlete-raw -> raw Typst render block
typlete math -> math render block
typlete-math -> math render block
typlete-typst-math -> math render block

Typst source code remains source code:

```typst#set text(size: 12pt)#rect[hello]```

For inline formulas in MDsveX, use the Svelte component directly. The same math escaping applies:

The value is <TypstInlinesource="alpha + beta" />.

Markdown transform

import{transformTypleteMarkdown}from'typlete/markdown';constoutput=awaittransformTypleteMarkdown(markdown,{output: 'component'});

Output modes:

typeTypleteMarkdownOutput='component'|'html'|'markdown-image'|'asset';

Component output

Best for MDsveX/Svelte-aware Markdown pipelines.

awaittransformTypleteMarkdown(markdown,{output: 'component'});

Produces Svelte component tags:

<TypstBlocksource={'#rect[hello]'} inputMode="raw" />
<TypstBlocksource={'alpha + beta'} />

HTML output

Best for renderers that accept raw HTML/SVG.

awaittransformTypleteMarkdown(markdown,{output: 'html'});

This renders SVG immediately and inserts it into the Markdown output.

Markdown image output

Best for renderers that do not support raw HTML but accept Markdown images.

awaittransformTypleteMarkdown(markdown,{output: 'markdown-image'});

This renders SVG and inserts it as a data:image/svg+xml Markdown image.

Asset output

Best for static sites.

awaittransformTypleteMarkdown(markdown,{output: 'asset',assetDir: 'static/typlete',assetBaseUrl: '/typlete'});

This writes SVG files to assetDir and inserts normal Markdown image links.

MDsveX preprocessor

Run the Typlete preprocessor before MDsveX so namespaced Typlete render fences are converted to Svelte component tags before MDsveX compiles the document.

// svelte.config.jsimportadapterfrom'@sveltejs/adapter-auto';import{mdsvex}from'mdsvex';import{createTypstMdsvexPreprocessor}from'typlete/markdown';constconfig={extensions: ['.svelte','.svx','.md'],preprocess: [createTypstMdsvexPreprocessor({output: 'component'}),mdsvex({extensions: ['.svx','.md']})],compilerOptions: {experimental: {async: true}},kit: {adapter: adapter()}};exportdefaultconfig;

For output: 'component', the preprocessor injects this import when a document contains a rendered Typst fence:

<scriptlang="ts">import { TypstInline, TypstBlock } from'typlete';</script>

Disable injection only if another MDsveX hook already imports the components:

createTypstMdsvexPreprocessor({output: 'component',injectComponentImports: false});

Rendering options for Markdown

Markdown helpers accept the same render options as component/server rendering:

awaittransformTypleteMarkdown(markdown,{output: 'asset',assetDir: 'static/typlete',assetBaseUrl: '/typlete',preamble: '',textSize: '11pt',pageMargin: '0pt',cache: true});

Sanitizing SVG

Typlete inserts compiler-produced SVG inline. By default it strips embedded SVG <script> tags and common script-like SVG attributes from that output. This is output-level defense-in-depth, not a sandbox for arbitrary raw Typst input or external assets. For stricter SVG sanitizing, pass a sanitizer.

DOMPurify is optional:

import{createDomPurifySvgSanitizer}from'typlete/sanitizers/dompurify';constsanitize=awaitcreateDomPurifySvgSanitizer();

Server-side DOMPurify requires both dompurify and jsdom:

import{createServerDomPurifySvgSanitizer}from'typlete/server/dompurify';constsanitize=awaitcreateServerDomPurifySvgSanitizer();

Limitations

  • Component SSR depends on Svelte experimental async rendering.
  • Plain typst fences are never render instructions; use typlete-typst, typlete-raw, or typlete-math when Markdown should render Typst.
  • Math mode and typlete-math escape $ and # before wrapping the source in Typst math delimiters. Raw mode and preamble are passed to Typst unchanged.
  • CSS text-color inheritance requires inline SVG. SVG loaded through <img>, including markdown-image and asset Markdown output, does not inherit the parent document's color.
  • Server rendering temporarily guards Typst runtime fetches so SvelteKit does not track external runtime fetches during SSR.
  • html, markdown-image, and asset output modes pre-render SVG immediately and are not reactive on the client.

About

Typst + Svelte

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Typlete

Svelte 5 components and Markdown helpers for rendering Typst as SVG.

Requirements

  • Svelte >= 5.36
  • Svelte async rendering enabled: compilerOptions.experimental.async = true
  • Vite/SvelteKit-compatible asset handling for browser WASM imports
// svelte.config.jsexportdefault{compilerOptions: {experimental: {async: true}}};

Install

pnpm add typlete

Svelte usage

<scriptlang="ts">import { TypstInline, TypstBlock } from'typlete';</script>
<p>
Inline formula: <TypstInlinesource="integral_0^1 x^2 dif x" />
</p>
<TypstBlocksource="sum_(i=1)^n i = (n(n+1)) / 2" />

source is Typst math by default. Typlete wraps math input in Typst math delimiters and escapes delimiter-breaking characters ($ and #) before compiling. Do not include outer $...$; pass the formula body.

CSS color inheritance

The implicit foreground of inline SVG rendered by Typlete inherits the surrounding CSS color, just like normal text.

<pclass="secondary">
Energy: <TypstInlinesource="E = m c^2" />
</p>
<style>.secondary {color: var(--text-secondary);	}</style>

This also follows normal CSS state changes such as :hover, disabled states, theme classes, and inherited custom properties. No formula-specific color prop is required.

Math construction strokes inherit the same color too, including fraction bars, radical bars, overlines, and underlines.

Explicit Typst text colors still take precedence and are preserved in the generated SVG:

<TypstInlinesource="alpha + beta"preamble="#set text(fill: red)" />

Color inheritance applies when the SVG is embedded inline, including the Svelte components, the server helper when its SVG is inserted inline, and Markdown output: 'html'. SVG used as an external image (output: 'markdown-image', output: 'asset', or a normal <img>) cannot inherit color from the surrounding HTML document.

Raw Typst

Use inputMode="raw" for full Typst markup fragments that should not be wrapped as math. Raw mode passes the source to Typst unchanged, so user-controlled raw input needs separate policy around allowed Typst features and assets.

<TypstBlockinputMode="raw"source={'#rect(radius: 6pt, inset: 10pt)[#strong[Raw Typst]]'} />
<!-- math mode: Typlete wraps source in Typst math -->
<TypstInlinesource="alpha + beta" />
<!-- raw mode: Typlete passes source to Typst unchanged -->
<TypstInlineinputMode="raw"source={'$alpha + beta$'} />

inputMode="markup" is accepted as a compatibility alias for raw.

Props

Typst, TypstInline, and TypstBlock support these common props:

typeTypstMode='inline'|'block';typeTypstInputMode='math'|'raw'|'markup';typeTypstSvgSanitizer=(svg: string)=>string;
source?: string;
inputMode?: TypstInputMode;
preamble?: string;
textSize?: string;
pageMargin?: string;
cache?: boolean;
sanitize?: TypstSvgSanitizer;
ariaLabel?: string;
title?: string;
loadingLabel?: string;
throwOnError?: boolean;
errorMode?: 'badge'|'none';class?: string;

Typst also accepts mode. TypstInline and TypstBlock set mode automatically.

SSR and local runtime assets

During SSR/build, Typlete renders SVG on the server. After hydration, the browser keeps the server-rendered SVG and loads the Typst WASM runtime only when the formula has to be rendered again on the client.

Typlete ships its own copies of the compiler/renderer WASM modules and the default Typst text font set inside the package. Browser builds emit those files as application-local assets; SSR reads the same installed Typlete assets directly from disk and passes their bytes to the Typst runtime. Normal formula rendering therefore does not fetch runtime assets from a CDN, depend on transitive-package layout, or require consumers to copy WASM/fonts manually.

Raw Typst that explicitly imports external Typst packages can still perform network access according to the underlying Typst package registry. That is separate from Typlete's own runtime assets.

Error behavior

By default, render failures keep the last successful SVG visible and show a small error badge.

<TypstBlocksource={formula} errorMode="badge" />
<TypstBlocksource={formula} errorMode="none" />

Use throwOnError={true} when server-side render failures should fail the request/build instead of being converted to component state.

Server helper

import{renderTypstSvgServer}from'typlete/server';constsvg=awaitrenderTypstSvgServer({source: 'integral_0^1 x^2 dif x',mode: 'block',inputMode: 'math',preamble: '',textSize: '11pt',pageMargin: '0pt',cache: true});

Markdown blocks

Typlete only renders namespaced Typlete fences. Normal typst fences stay normal Markdown code blocks, so documentation can show Typst source without triggering rendering.

Raw Typst render block:

```typlete-typst#set text(size: 12pt)#rect[hello]```

Math render block. The content is treated as a formula body; delimiter-breaking $ and # characters are escaped before Typst compiles it:

```typlete-mathsum_(i=1)^n i = (n(n+1)) / 2```

Accepted render fence names:

typlete -> raw Typst render block
typlete raw -> raw Typst render block
typlete typst -> raw Typst render block
typlete-typst -> raw Typst render block
typlete-raw -> raw Typst render block
typlete math -> math render block
typlete-math -> math render block
typlete-typst-math -> math render block

Typst source code remains source code:

```typst#set text(size: 12pt)#rect[hello]```

For inline formulas in MDsveX, use the Svelte component directly. The same math escaping applies:

The value is <TypstInlinesource="alpha + beta" />.

Markdown transform

import{transformTypleteMarkdown}from'typlete/markdown';constoutput=awaittransformTypleteMarkdown(markdown,{output: 'component'});

Output modes:

typeTypleteMarkdownOutput='component'|'html'|'markdown-image'|'asset';

Component output

Best for MDsveX/Svelte-aware Markdown pipelines.

awaittransformTypleteMarkdown(markdown,{output: 'component'});

Produces Svelte component tags:

<TypstBlocksource={'#rect[hello]'} inputMode="raw" />
<TypstBlocksource={'alpha + beta'} />

HTML output

Best for renderers that accept raw HTML/SVG.

awaittransformTypleteMarkdown(markdown,{output: 'html'});

This renders SVG immediately and inserts it into the Markdown output.

Markdown image output

Best for renderers that do not support raw HTML but accept Markdown images.

awaittransformTypleteMarkdown(markdown,{output: 'markdown-image'});

This renders SVG and inserts it as a data:image/svg+xml Markdown image.

Asset output

Best for static sites.

awaittransformTypleteMarkdown(markdown,{output: 'asset',assetDir: 'static/typlete',assetBaseUrl: '/typlete'});

This writes SVG files to assetDir and inserts normal Markdown image links.

MDsveX preprocessor

Run the Typlete preprocessor before MDsveX so namespaced Typlete render fences are converted to Svelte component tags before MDsveX compiles the document.

// svelte.config.jsimportadapterfrom'@sveltejs/adapter-auto';import{mdsvex}from'mdsvex';import{createTypstMdsvexPreprocessor}from'typlete/markdown';constconfig={extensions: ['.svelte','.svx','.md'],preprocess: [createTypstMdsvexPreprocessor({output: 'component'}),mdsvex({extensions: ['.svx','.md']})],compilerOptions: {experimental: {async: true}},kit: {adapter: adapter()}};exportdefaultconfig;

For output: 'component', the preprocessor injects this import when a document contains a rendered Typst fence:

<scriptlang="ts">import { TypstInline, TypstBlock } from'typlete';</script>

Disable injection only if another MDsveX hook already imports the components:

createTypstMdsvexPreprocessor({output: 'component',injectComponentImports: false});

Rendering options for Markdown

Markdown helpers accept the same render options as component/server rendering:

awaittransformTypleteMarkdown(markdown,{output: 'asset',assetDir: 'static/typlete',assetBaseUrl: '/typlete',preamble: '',textSize: '11pt',pageMargin: '0pt',cache: true});

Sanitizing SVG

Typlete inserts compiler-produced SVG inline. By default it strips embedded SVG <script> tags and common script-like SVG attributes from that output. This is output-level defense-in-depth, not a sandbox for arbitrary raw Typst input or external assets. For stricter SVG sanitizing, pass a sanitizer.

DOMPurify is optional:

import{createDomPurifySvgSanitizer}from'typlete/sanitizers/dompurify';constsanitize=awaitcreateDomPurifySvgSanitizer();

Server-side DOMPurify requires both dompurify and jsdom:

import{createServerDomPurifySvgSanitizer}from'typlete/server/dompurify';constsanitize=awaitcreateServerDomPurifySvgSanitizer();

Limitations

  • Component SSR depends on Svelte experimental async rendering.
  • Plain typst fences are never render instructions; use typlete-typst, typlete-raw, or typlete-math when Markdown should render Typst.
  • Math mode and typlete-math escape $ and # before wrapping the source in Typst math delimiters. Raw mode and preamble are passed to Typst unchanged.
  • CSS text-color inheritance requires inline SVG. SVG loaded through <img>, including markdown-image and asset Markdown output, does not inherit the parent document's color.
  • Server rendering temporarily guards Typst runtime fetches so SvelteKit does not track external runtime fetches during SSR.
  • html, markdown-image, and asset output modes pre-render SVG immediately and are not reactive on the client.

About

Typst + Svelte

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Repository files navigation

Typlete

Svelte 5 components and Markdown helpers for rendering Typst as SVG.

Requirements

  • Svelte >= 5.36
  • Svelte async rendering enabled: compilerOptions.experimental.async = true
  • Vite/SvelteKit-compatible asset handling for browser WASM imports
// svelte.config.jsexportdefault{compilerOptions: {experimental: {async: true}}};

Install

pnpm add typlete

Svelte usage

<scriptlang="ts">import { TypstInline, TypstBlock } from'typlete';</script>
<p>
Inline formula: <TypstInlinesource="integral_0^1 x^2 dif x" />
</p>
<TypstBlocksource="sum_(i=1)^n i = (n(n+1)) / 2" />

source is Typst math by default. Typlete wraps math input in Typst math delimiters and escapes delimiter-breaking characters ($ and #) before compiling. Do not include outer $...$; pass the formula body.

CSS color inheritance

The implicit foreground of inline SVG rendered by Typlete inherits the surrounding CSS color, just like normal text.

<pclass="secondary">
Energy: <TypstInlinesource="E = m c^2" />
</p>
<style>.secondary {color: var(--text-secondary);	}</style>

This also follows normal CSS state changes such as :hover, disabled states, theme classes, and inherited custom properties. No formula-specific color prop is required.

Math construction strokes inherit the same color too, including fraction bars, radical bars, overlines, and underlines.

Explicit Typst text colors still take precedence and are preserved in the generated SVG:

<TypstInlinesource="alpha + beta"preamble="#set text(fill: red)" />

Color inheritance applies when the SVG is embedded inline, including the Svelte components, the server helper when its SVG is inserted inline, and Markdown output: 'html'. SVG used as an external image (output: 'markdown-image', output: 'asset', or a normal <img>) cannot inherit color from the surrounding HTML document.

Raw Typst

Use inputMode="raw" for full Typst markup fragments that should not be wrapped as math. Raw mode passes the source to Typst unchanged, so user-controlled raw input needs separate policy around allowed Typst features and assets.

<TypstBlockinputMode="raw"source={'#rect(radius: 6pt, inset: 10pt)[#strong[Raw Typst]]'} />
<!-- math mode: Typlete wraps source in Typst math -->
<TypstInlinesource="alpha + beta" />
<!-- raw mode: Typlete passes source to Typst unchanged -->
<TypstInlineinputMode="raw"source={'$alpha + beta$'} />

inputMode="markup" is accepted as a compatibility alias for raw.

Props

Typst, TypstInline, and TypstBlock support these common props:

typeTypstMode='inline'|'block';typeTypstInputMode='math'|'raw'|'markup';typeTypstSvgSanitizer=(svg: string)=>string;
source?: string;
inputMode?: TypstInputMode;
preamble?: string;
textSize?: string;
pageMargin?: string;
cache?: boolean;
sanitize?: TypstSvgSanitizer;
ariaLabel?: string;
title?: string;
loadingLabel?: string;
throwOnError?: boolean;
errorMode?: 'badge'|'none';class?: string;

Typst also accepts mode. TypstInline and TypstBlock set mode automatically.

SSR and local runtime assets

During SSR/build, Typlete renders SVG on the server. After hydration, the browser keeps the server-rendered SVG and loads the Typst WASM runtime only when the formula has to be rendered again on the client.

Typlete ships its own copies of the compiler/renderer WASM modules and the default Typst text font set inside the package. Browser builds emit those files as application-local assets; SSR reads the same installed Typlete assets directly from disk and passes their bytes to the Typst runtime. Normal formula rendering therefore does not fetch runtime assets from a CDN, depend on transitive-package layout, or require consumers to copy WASM/fonts manually.

Raw Typst that explicitly imports external Typst packages can still perform network access according to the underlying Typst package registry. That is separate from Typlete's own runtime assets.

Error behavior

By default, render failures keep the last successful SVG visible and show a small error badge.

<TypstBlocksource={formula} errorMode="badge" />
<TypstBlocksource={formula} errorMode="none" />

Use throwOnError={true} when server-side render failures should fail the request/build instead of being converted to component state.

Server helper

import{renderTypstSvgServer}from'typlete/server';constsvg=awaitrenderTypstSvgServer({source: 'integral_0^1 x^2 dif x',mode: 'block',inputMode: 'math',preamble: '',textSize: '11pt',pageMargin: '0pt',cache: true});

Markdown blocks

Typlete only renders namespaced Typlete fences. Normal typst fences stay normal Markdown code blocks, so documentation can show Typst source without triggering rendering.

Raw Typst render block:

```typlete-typst#set text(size: 12pt)#rect[hello]```

Math render block. The content is treated as a formula body; delimiter-breaking $ and # characters are escaped before Typst compiles it:

```typlete-mathsum_(i=1)^n i = (n(n+1)) / 2```

Accepted render fence names:

typlete -> raw Typst render block
typlete raw -> raw Typst render block
typlete typst -> raw Typst render block
typlete-typst -> raw Typst render block
typlete-raw -> raw Typst render block
typlete math -> math render block
typlete-math -> math render block
typlete-typst-math -> math render block

Typst source code remains source code:

```typst#set text(size: 12pt)#rect[hello]```

For inline formulas in MDsveX, use the Svelte component directly. The same math escaping applies:

The value is <TypstInlinesource="alpha + beta" />.

Markdown transform

import{transformTypleteMarkdown}from'typlete/markdown';constoutput=awaittransformTypleteMarkdown(markdown,{output: 'component'});

Output modes:

typeTypleteMarkdownOutput='component'|'html'|'markdown-image'|'asset';

Component output

Best for MDsveX/Svelte-aware Markdown pipelines.

awaittransformTypleteMarkdown(markdown,{output: 'component'});

Produces Svelte component tags:

<TypstBlocksource={'#rect[hello]'} inputMode="raw" />
<TypstBlocksource={'alpha + beta'} />

HTML output

Best for renderers that accept raw HTML/SVG.

awaittransformTypleteMarkdown(markdown,{output: 'html'});

This renders SVG immediately and inserts it into the Markdown output.

Markdown image output

Best for renderers that do not support raw HTML but accept Markdown images.

awaittransformTypleteMarkdown(markdown,{output: 'markdown-image'});

This renders SVG and inserts it as a data:image/svg+xml Markdown image.

Asset output

Best for static sites.

awaittransformTypleteMarkdown(markdown,{output: 'asset',assetDir: 'static/typlete',assetBaseUrl: '/typlete'});

This writes SVG files to assetDir and inserts normal Markdown image links.

MDsveX preprocessor

Run the Typlete preprocessor before MDsveX so namespaced Typlete render fences are converted to Svelte component tags before MDsveX compiles the document.

// svelte.config.jsimportadapterfrom'@sveltejs/adapter-auto';import{mdsvex}from'mdsvex';import{createTypstMdsvexPreprocessor}from'typlete/markdown';constconfig={extensions: ['.svelte','.svx','.md'],preprocess: [createTypstMdsvexPreprocessor({output: 'component'}),mdsvex({extensions: ['.svx','.md']})],compilerOptions: {experimental: {async: true}},kit: {adapter: adapter()}};exportdefaultconfig;

For output: 'component', the preprocessor injects this import when a document contains a rendered Typst fence:

<scriptlang="ts">import { TypstInline, TypstBlock } from'typlete';</script>

Disable injection only if another MDsveX hook already imports the components:

createTypstMdsvexPreprocessor({output: 'component',injectComponentImports: false});

Rendering options for Markdown

Markdown helpers accept the same render options as component/server rendering:

awaittransformTypleteMarkdown(markdown,{output: 'asset',assetDir: 'static/typlete',assetBaseUrl: '/typlete',preamble: '',textSize: '11pt',pageMargin: '0pt',cache: true});

Sanitizing SVG

Typlete inserts compiler-produced SVG inline. By default it strips embedded SVG <script> tags and common script-like SVG attributes from that output. This is output-level defense-in-depth, not a sandbox for arbitrary raw Typst input or external assets. For stricter SVG sanitizing, pass a sanitizer.

DOMPurify is optional:

import{createDomPurifySvgSanitizer}from'typlete/sanitizers/dompurify';constsanitize=awaitcreateDomPurifySvgSanitizer();

Server-side DOMPurify requires both dompurify and jsdom:

import{createServerDomPurifySvgSanitizer}from'typlete/server/dompurify';constsanitize=awaitcreateServerDomPurifySvgSanitizer();

Limitations

  • Component SSR depends on Svelte experimental async rendering.
  • Plain typst fences are never render instructions; use typlete-typst, typlete-raw, or typlete-math when Markdown should render Typst.
  • Math mode and typlete-math escape $ and # before wrapping the source in Typst math delimiters. Raw mode and preamble are passed to Typst unchanged.
  • CSS text-color inheritance requires inline SVG. SVG loaded through <img>, including markdown-image and asset Markdown output, does not inherit the parent document's color.
  • Server rendering temporarily guards Typst runtime fetches so SvelteKit does not track external runtime fetches during SSR.
  • html, markdown-image, and asset output modes pre-render SVG immediately and are not reactive on the client.

About

Typst + Svelte

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Typlete

Svelte 5 components and Markdown helpers for rendering Typst as SVG.

Requirements

  • Svelte >= 5.36
  • Svelte async rendering enabled: compilerOptions.experimental.async = true
  • Vite/SvelteKit-compatible asset handling for browser WASM imports
// svelte.config.jsexportdefault{compilerOptions: {experimental: {async: true}}};

Install

pnpm add typlete

Svelte usage

<scriptlang="ts">import { TypstInline, TypstBlock } from'typlete';</script>
<p>
Inline formula: <TypstInlinesource="integral_0^1 x^2 dif x" />
</p>
<TypstBlocksource="sum_(i=1)^n i = (n(n+1)) / 2" />

source is Typst math by default. Typlete wraps math input in Typst math delimiters and escapes delimiter-breaking characters ($ and #) before compiling. Do not include outer $...$; pass the formula body.

CSS color inheritance

The implicit foreground of inline SVG rendered by Typlete inherits the surrounding CSS color, just like normal text.

<pclass="secondary">
Energy: <TypstInlinesource="E = m c^2" />
</p>
<style>.secondary {color: var(--text-secondary);	}</style>

This also follows normal CSS state changes such as :hover, disabled states, theme classes, and inherited custom properties. No formula-specific color prop is required.

Math construction strokes inherit the same color too, including fraction bars, radical bars, overlines, and underlines.

Explicit Typst text colors still take precedence and are preserved in the generated SVG:

<TypstInlinesource="alpha + beta"preamble="#set text(fill: red)" />

Color inheritance applies when the SVG is embedded inline, including the Svelte components, the server helper when its SVG is inserted inline, and Markdown output: 'html'. SVG used as an external image (output: 'markdown-image', output: 'asset', or a normal <img>) cannot inherit color from the surrounding HTML document.

Raw Typst

Use inputMode="raw" for full Typst markup fragments that should not be wrapped as math. Raw mode passes the source to Typst unchanged, so user-controlled raw input needs separate policy around allowed Typst features and assets.

<TypstBlockinputMode="raw"source={'#rect(radius: 6pt, inset: 10pt)[#strong[Raw Typst]]'} />
<!-- math mode: Typlete wraps source in Typst math -->
<TypstInlinesource="alpha + beta" />
<!-- raw mode: Typlete passes source to Typst unchanged -->
<TypstInlineinputMode="raw"source={'$alpha + beta$'} />

inputMode="markup" is accepted as a compatibility alias for raw.

Props

Typst, TypstInline, and TypstBlock support these common props:

typeTypstMode='inline'|'block';typeTypstInputMode='math'|'raw'|'markup';typeTypstSvgSanitizer=(svg: string)=>string;
source?: string;
inputMode?: TypstInputMode;
preamble?: string;
textSize?: string;
pageMargin?: string;
cache?: boolean;
sanitize?: TypstSvgSanitizer;
ariaLabel?: string;
title?: string;
loadingLabel?: string;
throwOnError?: boolean;
errorMode?: 'badge'|'none';class?: string;

Typst also accepts mode. TypstInline and TypstBlock set mode automatically.

SSR and local runtime assets

During SSR/build, Typlete renders SVG on the server. After hydration, the browser keeps the server-rendered SVG and loads the Typst WASM runtime only when the formula has to be rendered again on the client.

Typlete ships its own copies of the compiler/renderer WASM modules and the default Typst text font set inside the package. Browser builds emit those files as application-local assets; SSR reads the same installed Typlete assets directly from disk and passes their bytes to the Typst runtime. Normal formula rendering therefore does not fetch runtime assets from a CDN, depend on transitive-package layout, or require consumers to copy WASM/fonts manually.

Raw Typst that explicitly imports external Typst packages can still perform network access according to the underlying Typst package registry. That is separate from Typlete's own runtime assets.

Error behavior

By default, render failures keep the last successful SVG visible and show a small error badge.

<TypstBlocksource={formula} errorMode="badge" />
<TypstBlocksource={formula} errorMode="none" />

Use throwOnError={true} when server-side render failures should fail the request/build instead of being converted to component state.

Server helper

import{renderTypstSvgServer}from'typlete/server';constsvg=awaitrenderTypstSvgServer({source: 'integral_0^1 x^2 dif x',mode: 'block',inputMode: 'math',preamble: '',textSize: '11pt',pageMargin: '0pt',cache: true});

Markdown blocks

Typlete only renders namespaced Typlete fences. Normal typst fences stay normal Markdown code blocks, so documentation can show Typst source without triggering rendering.

Raw Typst render block:

```typlete-typst#set text(size: 12pt)#rect[hello]```

Math render block. The content is treated as a formula body; delimiter-breaking $ and # characters are escaped before Typst compiles it:

```typlete-mathsum_(i=1)^n i = (n(n+1)) / 2```

Accepted render fence names:

typlete -> raw Typst render block
typlete raw -> raw Typst render block
typlete typst -> raw Typst render block
typlete-typst -> raw Typst render block
typlete-raw -> raw Typst render block
typlete math -> math render block
typlete-math -> math render block
typlete-typst-math -> math render block

Typst source code remains source code:

```typst#set text(size: 12pt)#rect[hello]```

For inline formulas in MDsveX, use the Svelte component directly. The same math escaping applies:

The value is <TypstInlinesource="alpha + beta" />.

Markdown transform

import{transformTypleteMarkdown}from'typlete/markdown';constoutput=awaittransformTypleteMarkdown(markdown,{output: 'component'});

Output modes:

typeTypleteMarkdownOutput='component'|'html'|'markdown-image'|'asset';

Component output

Best for MDsveX/Svelte-aware Markdown pipelines.

awaittransformTypleteMarkdown(markdown,{output: 'component'});

Produces Svelte component tags:

<TypstBlocksource={'#rect[hello]'} inputMode="raw" />
<TypstBlocksource={'alpha + beta'} />

HTML output

Best for renderers that accept raw HTML/SVG.

awaittransformTypleteMarkdown(markdown,{output: 'html'});

This renders SVG immediately and inserts it into the Markdown output.

Markdown image output

Best for renderers that do not support raw HTML but accept Markdown images.

awaittransformTypleteMarkdown(markdown,{output: 'markdown-image'});

This renders SVG and inserts it as a data:image/svg+xml Markdown image.

Asset output

Best for static sites.

awaittransformTypleteMarkdown(markdown,{output: 'asset',assetDir: 'static/typlete',assetBaseUrl: '/typlete'});

This writes SVG files to assetDir and inserts normal Markdown image links.

MDsveX preprocessor

Run the Typlete preprocessor before MDsveX so namespaced Typlete render fences are converted to Svelte component tags before MDsveX compiles the document.

// svelte.config.jsimportadapterfrom'@sveltejs/adapter-auto';import{mdsvex}from'mdsvex';import{createTypstMdsvexPreprocessor}from'typlete/markdown';constconfig={extensions: ['.svelte','.svx','.md'],preprocess: [createTypstMdsvexPreprocessor({output: 'component'}),mdsvex({extensions: ['.svx','.md']})],compilerOptions: {experimental: {async: true}},kit: {adapter: adapter()}};exportdefaultconfig;

For output: 'component', the preprocessor injects this import when a document contains a rendered Typst fence:

<scriptlang="ts">import { TypstInline, TypstBlock } from'typlete';</script>

Disable injection only if another MDsveX hook already imports the components:

createTypstMdsvexPreprocessor({output: 'component',injectComponentImports: false});

Rendering options for Markdown

Markdown helpers accept the same render options as component/server rendering:

awaittransformTypleteMarkdown(markdown,{output: 'asset',assetDir: 'static/typlete',assetBaseUrl: '/typlete',preamble: '',textSize: '11pt',pageMargin: '0pt',cache: true});

Sanitizing SVG

Typlete inserts compiler-produced SVG inline. By default it strips embedded SVG <script> tags and common script-like SVG attributes from that output. This is output-level defense-in-depth, not a sandbox for arbitrary raw Typst input or external assets. For stricter SVG sanitizing, pass a sanitizer.

DOMPurify is optional:

import{createDomPurifySvgSanitizer}from'typlete/sanitizers/dompurify';constsanitize=awaitcreateDomPurifySvgSanitizer();

Server-side DOMPurify requires both dompurify and jsdom:

import{createServerDomPurifySvgSanitizer}from'typlete/server/dompurify';constsanitize=awaitcreateServerDomPurifySvgSanitizer();

Limitations

  • Component SSR depends on Svelte experimental async rendering.
  • Plain typst fences are never render instructions; use typlete-typst, typlete-raw, or typlete-math when Markdown should render Typst.
  • Math mode and typlete-math escape $ and # before wrapping the source in Typst math delimiters. Raw mode and preamble are passed to Typst unchanged.
  • CSS text-color inheritance requires inline SVG. SVG loaded through <img>, including markdown-image and asset Markdown output, does not inherit the parent document's color.
  • Server rendering temporarily guards Typst runtime fetches so SvelteKit does not track external runtime fetches during SSR.
  • html, markdown-image, and asset output modes pre-render SVG immediately and are not reactive on the client.

About

Typst + Svelte

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Typlete

Svelte 5 components and Markdown helpers for rendering Typst as SVG.

Requirements

  • Svelte >= 5.36
  • Svelte async rendering enabled: compilerOptions.experimental.async = true
  • Vite/SvelteKit-compatible asset handling for browser WASM imports
// svelte.config.jsexportdefault{compilerOptions: {experimental: {async: true}}};

Install

pnpm add typlete

Svelte usage

<scriptlang="ts">import { TypstInline, TypstBlock } from'typlete';</script>
<p>
Inline formula: <TypstInlinesource="integral_0^1 x^2 dif x" />
</p>
<TypstBlocksource="sum_(i=1)^n i = (n(n+1)) / 2" />

source is Typst math by default. Typlete wraps math input in Typst math delimiters and escapes delimiter-breaking characters ($ and #) before compiling. Do not include outer $...$; pass the formula body.

CSS color inheritance

The implicit foreground of inline SVG rendered by Typlete inherits the surrounding CSS color, just like normal text.

<pclass="secondary">
Energy: <TypstInlinesource="E = m c^2" />
</p>
<style>.secondary {color: var(--text-secondary);	}</style>

This also follows normal CSS state changes such as :hover, disabled states, theme classes, and inherited custom properties. No formula-specific color prop is required.

Math construction strokes inherit the same color too, including fraction bars, radical bars, overlines, and underlines.

Explicit Typst text colors still take precedence and are preserved in the generated SVG:

<TypstInlinesource="alpha + beta"preamble="#set text(fill: red)" />

Color inheritance applies when the SVG is embedded inline, including the Svelte components, the server helper when its SVG is inserted inline, and Markdown output: 'html'. SVG used as an external image (output: 'markdown-image', output: 'asset', or a normal <img>) cannot inherit color from the surrounding HTML document.

Raw Typst

Use inputMode="raw" for full Typst markup fragments that should not be wrapped as math. Raw mode passes the source to Typst unchanged, so user-controlled raw input needs separate policy around allowed Typst features and assets.

<TypstBlockinputMode="raw"source={'#rect(radius: 6pt, inset: 10pt)[#strong[Raw Typst]]'} />
<!-- math mode: Typlete wraps source in Typst math -->
<TypstInlinesource="alpha + beta" />
<!-- raw mode: Typlete passes source to Typst unchanged -->
<TypstInlineinputMode="raw"source={'$alpha + beta$'} />

inputMode="markup" is accepted as a compatibility alias for raw.

Props

Typst, TypstInline, and TypstBlock support these common props:

typeTypstMode='inline'|'block';typeTypstInputMode='math'|'raw'|'markup';typeTypstSvgSanitizer=(svg: string)=>string;
source?: string;
inputMode?: TypstInputMode;
preamble?: string;
textSize?: string;
pageMargin?: string;
cache?: boolean;
sanitize?: TypstSvgSanitizer;
ariaLabel?: string;
title?: string;
loadingLabel?: string;
throwOnError?: boolean;
errorMode?: 'badge'|'none';class?: string;

Typst also accepts mode. TypstInline and TypstBlock set mode automatically.

SSR and local runtime assets

During SSR/build, Typlete renders SVG on the server. After hydration, the browser keeps the server-rendered SVG and loads the Typst WASM runtime only when the formula has to be rendered again on the client.

Typlete ships its own copies of the compiler/renderer WASM modules and the default Typst text font set inside the package. Browser builds emit those files as application-local assets; SSR reads the same installed Typlete assets directly from disk and passes their bytes to the Typst runtime. Normal formula rendering therefore does not fetch runtime assets from a CDN, depend on transitive-package layout, or require consumers to copy WASM/fonts manually.

Raw Typst that explicitly imports external Typst packages can still perform network access according to the underlying Typst package registry. That is separate from Typlete's own runtime assets.

Error behavior

By default, render failures keep the last successful SVG visible and show a small error badge.

<TypstBlocksource={formula} errorMode="badge" />
<TypstBlocksource={formula} errorMode="none" />

Use throwOnError={true} when server-side render failures should fail the request/build instead of being converted to component state.

Server helper

import{renderTypstSvgServer}from'typlete/server';constsvg=awaitrenderTypstSvgServer({source: 'integral_0^1 x^2 dif x',mode: 'block',inputMode: 'math',preamble: '',textSize: '11pt',pageMargin: '0pt',cache: true});

Markdown blocks

Typlete only renders namespaced Typlete fences. Normal typst fences stay normal Markdown code blocks, so documentation can show Typst source without triggering rendering.

Raw Typst render block:

```typlete-typst#set text(size: 12pt)#rect[hello]```

Math render block. The content is treated as a formula body; delimiter-breaking $ and # characters are escaped before Typst compiles it:

```typlete-mathsum_(i=1)^n i = (n(n+1)) / 2```

Accepted render fence names:

typlete -> raw Typst render block
typlete raw -> raw Typst render block
typlete typst -> raw Typst render block
typlete-typst -> raw Typst render block
typlete-raw -> raw Typst render block
typlete math -> math render block
typlete-math -> math render block
typlete-typst-math -> math render block

Typst source code remains source code:

```typst#set text(size: 12pt)#rect[hello]```

For inline formulas in MDsveX, use the Svelte component directly. The same math escaping applies:

The value is <TypstInlinesource="alpha + beta" />.

Markdown transform

import{transformTypleteMarkdown}from'typlete/markdown';constoutput=awaittransformTypleteMarkdown(markdown,{output: 'component'});

Output modes:

typeTypleteMarkdownOutput='component'|'html'|'markdown-image'|'asset';

Component output

Best for MDsveX/Svelte-aware Markdown pipelines.

awaittransformTypleteMarkdown(markdown,{output: 'component'});

Produces Svelte component tags:

<TypstBlocksource={'#rect[hello]'} inputMode="raw" />
<TypstBlocksource={'alpha + beta'} />

HTML output

Best for renderers that accept raw HTML/SVG.

awaittransformTypleteMarkdown(markdown,{output: 'html'});

This renders SVG immediately and inserts it into the Markdown output.

Markdown image output

Best for renderers that do not support raw HTML but accept Markdown images.

awaittransformTypleteMarkdown(markdown,{output: 'markdown-image'});

This renders SVG and inserts it as a data:image/svg+xml Markdown image.

Asset output

Best for static sites.

awaittransformTypleteMarkdown(markdown,{output: 'asset',assetDir: 'static/typlete',assetBaseUrl: '/typlete'});

This writes SVG files to assetDir and inserts normal Markdown image links.

MDsveX preprocessor

Run the Typlete preprocessor before MDsveX so namespaced Typlete render fences are converted to Svelte component tags before MDsveX compiles the document.

// svelte.config.jsimportadapterfrom'@sveltejs/adapter-auto';import{mdsvex}from'mdsvex';import{createTypstMdsvexPreprocessor}from'typlete/markdown';constconfig={extensions: ['.svelte','.svx','.md'],preprocess: [createTypstMdsvexPreprocessor({output: 'component'}),mdsvex({extensions: ['.svx','.md']})],compilerOptions: {experimental: {async: true}},kit: {adapter: adapter()}};exportdefaultconfig;

For output: 'component', the preprocessor injects this import when a document contains a rendered Typst fence:

<scriptlang="ts">import { TypstInline, TypstBlock } from'typlete';</script>

Disable injection only if another MDsveX hook already imports the components:

createTypstMdsvexPreprocessor({output: 'component',injectComponentImports: false});

Rendering options for Markdown

Markdown helpers accept the same render options as component/server rendering:

awaittransformTypleteMarkdown(markdown,{output: 'asset',assetDir: 'static/typlete',assetBaseUrl: '/typlete',preamble: '',textSize: '11pt',pageMargin: '0pt',cache: true});

Sanitizing SVG

Typlete inserts compiler-produced SVG inline. By default it strips embedded SVG <script> tags and common script-like SVG attributes from that output. This is output-level defense-in-depth, not a sandbox for arbitrary raw Typst input or external assets. For stricter SVG sanitizing, pass a sanitizer.

DOMPurify is optional:

import{createDomPurifySvgSanitizer}from'typlete/sanitizers/dompurify';constsanitize=awaitcreateDomPurifySvgSanitizer();

Server-side DOMPurify requires both dompurify and jsdom:

import{createServerDomPurifySvgSanitizer}from'typlete/server/dompurify';constsanitize=awaitcreateServerDomPurifySvgSanitizer();

Limitations

  • Component SSR depends on Svelte experimental async rendering.
  • Plain typst fences are never render instructions; use typlete-typst, typlete-raw, or typlete-math when Markdown should render Typst.
  • Math mode and typlete-math escape $ and # before wrapping the source in Typst math delimiters. Raw mode and preamble are passed to Typst unchanged.
  • CSS text-color inheritance requires inline SVG. SVG loaded through <img>, including markdown-image and asset Markdown output, does not inherit the parent document's color.
  • Server rendering temporarily guards Typst runtime fetches so SvelteKit does not track external runtime fetches during SSR.
  • html, markdown-image, and asset output modes pre-render SVG immediately and are not reactive on the client.

About

Typst + Svelte

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Repository files navigation

Typlete

Svelte 5 components and Markdown helpers for rendering Typst as SVG.

Requirements

  • Svelte >= 5.36
  • Svelte async rendering enabled: compilerOptions.experimental.async = true
  • Vite/SvelteKit-compatible asset handling for browser WASM imports
// svelte.config.jsexportdefault{compilerOptions: {experimental: {async: true}}};

Install

pnpm add typlete

Svelte usage

<scriptlang="ts">import { TypstInline, TypstBlock } from'typlete';</script>
<p>
Inline formula: <TypstInlinesource="integral_0^1 x^2 dif x" />
</p>
<TypstBlocksource="sum_(i=1)^n i = (n(n+1)) / 2" />

source is Typst math by default. Typlete wraps math input in Typst math delimiters and escapes delimiter-breaking characters ($ and #) before compiling. Do not include outer $...$; pass the formula body.

CSS color inheritance

The implicit foreground of inline SVG rendered by Typlete inherits the surrounding CSS color, just like normal text.

<pclass="secondary">
Energy: <TypstInlinesource="E = m c^2" />
</p>
<style>.secondary {color: var(--text-secondary);	}</style>

This also follows normal CSS state changes such as :hover, disabled states, theme classes, and inherited custom properties. No formula-specific color prop is required.

Math construction strokes inherit the same color too, including fraction bars, radical bars, overlines, and underlines.

Explicit Typst text colors still take precedence and are preserved in the generated SVG:

<TypstInlinesource="alpha + beta"preamble="#set text(fill: red)" />

Color inheritance applies when the SVG is embedded inline, including the Svelte components, the server helper when its SVG is inserted inline, and Markdown output: 'html'. SVG used as an external image (output: 'markdown-image', output: 'asset', or a normal <img>) cannot inherit color from the surrounding HTML document.

Raw Typst

Use inputMode="raw" for full Typst markup fragments that should not be wrapped as math. Raw mode passes the source to Typst unchanged, so user-controlled raw input needs separate policy around allowed Typst features and assets.

<TypstBlockinputMode="raw"source={'#rect(radius: 6pt, inset: 10pt)[#strong[Raw Typst]]'} />
<!-- math mode: Typlete wraps source in Typst math -->
<TypstInlinesource="alpha + beta" />
<!-- raw mode: Typlete passes source to Typst unchanged -->
<TypstInlineinputMode="raw"source={'$alpha + beta$'} />

inputMode="markup" is accepted as a compatibility alias for raw.

Props

Typst, TypstInline, and TypstBlock support these common props:

typeTypstMode='inline'|'block';typeTypstInputMode='math'|'raw'|'markup';typeTypstSvgSanitizer=(svg: string)=>string;
source?: string;
inputMode?: TypstInputMode;
preamble?: string;
textSize?: string;
pageMargin?: string;
cache?: boolean;
sanitize?: TypstSvgSanitizer;
ariaLabel?: string;
title?: string;
loadingLabel?: string;
throwOnError?: boolean;
errorMode?: 'badge'|'none';class?: string;

Typst also accepts mode. TypstInline and TypstBlock set mode automatically.

SSR and local runtime assets

During SSR/build, Typlete renders SVG on the server. After hydration, the browser keeps the server-rendered SVG and loads the Typst WASM runtime only when the formula has to be rendered again on the client.

Typlete ships its own copies of the compiler/renderer WASM modules and the default Typst text font set inside the package. Browser builds emit those files as application-local assets; SSR reads the same installed Typlete assets directly from disk and passes their bytes to the Typst runtime. Normal formula rendering therefore does not fetch runtime assets from a CDN, depend on transitive-package layout, or require consumers to copy WASM/fonts manually.

Raw Typst that explicitly imports external Typst packages can still perform network access according to the underlying Typst package registry. That is separate from Typlete's own runtime assets.

Error behavior

By default, render failures keep the last successful SVG visible and show a small error badge.

<TypstBlocksource={formula} errorMode="badge" />
<TypstBlocksource={formula} errorMode="none" />

Use throwOnError={true} when server-side render failures should fail the request/build instead of being converted to component state.

Server helper

import{renderTypstSvgServer}from'typlete/server';constsvg=awaitrenderTypstSvgServer({source: 'integral_0^1 x^2 dif x',mode: 'block',inputMode: 'math',preamble: '',textSize: '11pt',pageMargin: '0pt',cache: true});

Markdown blocks

Typlete only renders namespaced Typlete fences. Normal typst fences stay normal Markdown code blocks, so documentation can show Typst source without triggering rendering.

Raw Typst render block:

```typlete-typst#set text(size: 12pt)#rect[hello]```

Math render block. The content is treated as a formula body; delimiter-breaking $ and # characters are escaped before Typst compiles it:

```typlete-mathsum_(i=1)^n i = (n(n+1)) / 2```

Accepted render fence names:

typlete -> raw Typst render block
typlete raw -> raw Typst render block
typlete typst -> raw Typst render block
typlete-typst -> raw Typst render block
typlete-raw -> raw Typst render block
typlete math -> math render block
typlete-math -> math render block
typlete-typst-math -> math render block

Typst source code remains source code:

```typst#set text(size: 12pt)#rect[hello]```

For inline formulas in MDsveX, use the Svelte component directly. The same math escaping applies:

The value is <TypstInlinesource="alpha + beta" />.

Markdown transform

import{transformTypleteMarkdown}from'typlete/markdown';constoutput=awaittransformTypleteMarkdown(markdown,{output: 'component'});

Output modes:

typeTypleteMarkdownOutput='component'|'html'|'markdown-image'|'asset';

Component output

Best for MDsveX/Svelte-aware Markdown pipelines.

awaittransformTypleteMarkdown(markdown,{output: 'component'});

Produces Svelte component tags:

<TypstBlocksource={'#rect[hello]'} inputMode="raw" />
<TypstBlocksource={'alpha + beta'} />

HTML output

Best for renderers that accept raw HTML/SVG.

awaittransformTypleteMarkdown(markdown,{output: 'html'});

This renders SVG immediately and inserts it into the Markdown output.

Markdown image output

Best for renderers that do not support raw HTML but accept Markdown images.

awaittransformTypleteMarkdown(markdown,{output: 'markdown-image'});

This renders SVG and inserts it as a data:image/svg+xml Markdown image.

Asset output

Best for static sites.

awaittransformTypleteMarkdown(markdown,{output: 'asset',assetDir: 'static/typlete',assetBaseUrl: '/typlete'});

This writes SVG files to assetDir and inserts normal Markdown image links.

MDsveX preprocessor

Run the Typlete preprocessor before MDsveX so namespaced Typlete render fences are converted to Svelte component tags before MDsveX compiles the document.

// svelte.config.jsimportadapterfrom'@sveltejs/adapter-auto';import{mdsvex}from'mdsvex';import{createTypstMdsvexPreprocessor}from'typlete/markdown';constconfig={extensions: ['.svelte','.svx','.md'],preprocess: [createTypstMdsvexPreprocessor({output: 'component'}),mdsvex({extensions: ['.svx','.md']})],compilerOptions: {experimental: {async: true}},kit: {adapter: adapter()}};exportdefaultconfig;

For output: 'component', the preprocessor injects this import when a document contains a rendered Typst fence:

<scriptlang="ts">import { TypstInline, TypstBlock } from'typlete';</script>

Disable injection only if another MDsveX hook already imports the components:

createTypstMdsvexPreprocessor({output: 'component',injectComponentImports: false});

Rendering options for Markdown

Markdown helpers accept the same render options as component/server rendering:

awaittransformTypleteMarkdown(markdown,{output: 'asset',assetDir: 'static/typlete',assetBaseUrl: '/typlete',preamble: '',textSize: '11pt',pageMargin: '0pt',cache: true});

Sanitizing SVG

Typlete inserts compiler-produced SVG inline. By default it strips embedded SVG <script> tags and common script-like SVG attributes from that output. This is output-level defense-in-depth, not a sandbox for arbitrary raw Typst input or external assets. For stricter SVG sanitizing, pass a sanitizer.

DOMPurify is optional:

import{createDomPurifySvgSanitizer}from'typlete/sanitizers/dompurify';constsanitize=awaitcreateDomPurifySvgSanitizer();

Server-side DOMPurify requires both dompurify and jsdom:

import{createServerDomPurifySvgSanitizer}from'typlete/server/dompurify';constsanitize=awaitcreateServerDomPurifySvgSanitizer();

Limitations

  • Component SSR depends on Svelte experimental async rendering.
  • Plain typst fences are never render instructions; use typlete-typst, typlete-raw, or typlete-math when Markdown should render Typst.
  • Math mode and typlete-math escape $ and # before wrapping the source in Typst math delimiters. Raw mode and preamble are passed to Typst unchanged.
  • CSS text-color inheritance requires inline SVG. SVG loaded through <img>, including markdown-image and asset Markdown output, does not inherit the parent document's color.
  • Server rendering temporarily guards Typst runtime fetches so SvelteKit does not track external runtime fetches during SSR.
  • html, markdown-image, and asset output modes pre-render SVG immediately and are not reactive on the client.

About

Typst + Svelte

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages