fuz_code

syntax styling utilities and components for TypeScript, Svelte, Markdown, and more 🎨

repo 0.48.0 npm

npm i -D @fuzdev/fuz_code

usage #

fuz_code highlights code with one lexer per language without regular expressions, generating HTML with .token_* classes that a theme styles. The Code Svelte component is the typical entry point.

To use it, import the default theme or your own:

// +layout.svelte import '@fuzdev/fuz_code/theme.css'; // add this

then use Code:

<script> // Something.svelte import Code from '@fuzdev/fuz_code/Code.svelte'; </script> <Code content="<header>hello world</header>" />

outputs:

<header>hello world</header>

Dependencies
#

By default fuz_code depends on fuz_css to provide color-scheme-aware color variables. If you're not using it, import theme_variables.css or bring your own:

// +layout.svelte import '@fuzdev/fuz_code/theme.css'; // if not using fuz_css, add this too: import '@fuzdev/fuz_code/theme_variables.css';

Preprocessor
#

The svelte_preprocess_fuz_code preprocessor compiles static Code content at build time, replacing runtime syntax highlighting:

// svelte.config.js import {svelte_preprocess_fuz_code} from '@fuzdev/fuz_code/svelte_preprocess_fuz_code.js'; export default { preprocess: [ svelte_preprocess_fuz_code(), vitePreprocess(), ], };

Static string content props are highlighted at build time and replaced with pre-rendered HTML. Dynamic content is left unchanged for runtime highlighting.

Svelte support
#

Code styles Svelte by default, originally based on prism-svelte by @pngwn (pngwn.at):

<Code content="<scr..." />

styled:

<script lang="ts"> import Card from '@fuzdev/fuz_ui/Card.svelte'; console.log('hello Card', Card); </script> <Card> <div class="greeting">hi {friend}</div> </Card>

TypeScript support
#

Code supports TypeScript with lang="ts":

<Code lang="ts" content="export type A<T> = ('b' | 3) & T;" />
export type A<T> = ('b' | 3) & T;

Markdown support
#

Code supports Markdown with lang="md", and fenced blocks for all languages:

<Code lang="md" content="# hello `world` ..." />
# hello `world` ```ts const a = 1; ```

Disabling styling
#

Passing lang={null} disables syntax styling:

<Code lang={null} content="<aside>all is gray</aside>" /> <aside>all is gray</aside>

Layout
#

Code is a block by default:

abc
<div>ab<Code content="c" /></div>

It can be inlined with <Code inline content="..." />

Programmatic usage
#

fuz_code can be used directly from TypeScript without Svelte. Import syntax_styler_global for a pre-configured instance with all built-in languages:

import {syntax_styler_global} from '@fuzdev/fuz_code/syntax_styler_global.js'; const html = syntax_styler_global.stylize('const x: number = 42;', 'ts');

returns HTML string:

<span class="token_keyword">const</span> x<span class="token_type_annotation"><span class="token_:">:</span> <span class="token_type"><span class="token_builtin">number</span></span></span> <span class="token_operator">=</span> <span class="token_number">42</span><span class="token_punctuation">;</span>

then rendered with:

<code data-lang="ts">{@html programmatic_result}</code>

we get:

const x: number = 42;

For a custom configuration, create your own SyntaxStyler and register only the languages you need:

import {SyntaxStyler} from '@fuzdev/fuz_code/syntax_styler.js'; import {lexer_markup} from '@fuzdev/fuz_code/lexer_markup.js'; import {lexer_css} from '@fuzdev/fuz_code/lexer_css.js'; const styler = new SyntaxStyler(); styler.add_lang(lexer_markup); styler.add_lang(lexer_css); const html = styler.stylize('<div class="example">hello</div>', 'html');

Experimental highlighting API
#

fuz_code also has experimental support for the CSS Custom Highlight API with CodeHighlight — see the samples and textarea.

samples #

Syntax-highlighted samples in every supported language, rendered with Code and the experimental CodeHighlight.

textarea #

An experimental editable, live-highlighted textarea backed by CodeTextarea.

benchmark #

fuz_code runs one hand-written single-pass lexer per language without regular expressions, emitting a flat token event stream that renders to HTML in one forward pass. There is no grammar interpreter and no backtracking, so highlighting cost stays linear in the length of the input, including partial or malformed source.

It is optimized for runtime highlighting, including streaming use cases. Shiki targets build-time use and runs the Oniguruma regexp engine that TextMate grammars require, trading runtime speed for grammar and theme coverage. fuz_code trades that coverage for a small, fast runtime path — it also provides tools for pre-compilation, and you can bring your own lexers for languages it doesn't support.

Compared to Shiki and Prism
#

The cross-implementation benchmark measures fuz_code against Prism and Shiki (both the JavaScript and Oniguruma engines). For end-to-end stylize — lexing plus HTML generation, the realistic runtime path — fuz_code runs roughly an order of magnitude faster than Prism and about two orders of magnitude faster than Shiki:

languagefuz_code vs Prismfuz_code vs Shiki
ts~18×~140×
css~8×~90×
html~11×~80×
json~17×~95×
svelte~14×~165×

Representative stylize results on larger inputs from one machine; absolute numbers vary by hardware. See the committed comparison results for the full matrix across engines and sizes, plus tokenize-only rows that compare the raw lexers without HTML generation.

In the browser
#

The in-browser benchmark measures real DOM rendering rather than pure compute. It times fuz_code's two renderers — the standard HTML path (Code) and the experimental CSS Custom Highlight API path (CodeHighlight, ranges) — across every supported language, reporting mean, median, percentiles, coefficient of variation, and throughput, with system-stability gating between samples.

Set the iteration count, warmup runs, cooldown, and content multiplier, then run it on your own hardware. For the steadiest numbers, launch Chromium with garbage collection exposed (chromium --js-flags="--expose-gc") so the harness can settle the heap between samples.

A sample run of work time — stylize plus DOM commit — per language for each renderer. The default Code path builds a .token_* span per token; the experimental CodeHighlight path skips that DOM, so it commits less:

Code (html) CodeHighlight (ranges)
ts
91ms
12ms
css
11ms
3ms
html
28ms
4ms
json
11ms
2ms
svelte
91ms
13ms
md
69ms
10ms
sh
49ms
8ms

Lower is better. The benchmark uses complex samples at the tool's default size, so this is illustrative, not representative of most inputs. The live tool also reports paint-settle time, percentiles, and throughput.

On your machine
#

The command-line benchmarks run from a checkout of the repo:

  • npm run benchmark — the internal suite, timing every sample at normal and 100× sizes against a local baseline for regression detection.
  • npm run benchmark:vs — the cross-implementation shootout against Prism and Shiki that produces the comparison results above.

The internal suite includes a pathological group of adversarial inputs (deeply nested and degenerate source) that pins the lexer's worst-case behavior to linear time; the same generators back the linearity tests. The engine keeps no resumable state, so it re-lexes the whole document on every change rather than tokenizing incrementally — whole-document lexing is already sub-frame at these speeds.

Why it's fast
#

  • No regular expressions — char-code scanning, native indexOf, keyword maps.
  • One single-pass lexer per language emitting a flat Int32Array event stream, rendered to HTML in a single forward pass.
  • Linear-time by construction — every scan loop advances, so there is no catastrophic backtracking on adversarial input.
  • No runtime dependencies — nothing heavier than the lexers ships to the browser.

The same entry points power all of it: syntax_styler_global and the SyntaxStyler class expose lex (the flat event stream) and stylize (HTML). See the usage for the API and the samples for output in every language.

api #

Browse the full api docs.