Skip to content

Start with Rust

Bring editor-grade code highlighting to your Rust app. Start with the opt-in remote feature, load the languages you need, and render ready-to-use HTML.

Highlight your first snippet

Create a binary crate, add Ferriki with remote assets enabled, and save the program below as src/main.rs:

cargo new ferriki-demo
cd ferriki-demo
cargo add ferriki --features remote

The optional remote feature loads standard assets from Ferriki's release-pinned asset service. On first use, Ferriki downloads missing payloads, verifies their size and SHA-256 against the release manifest, then caches them.

use ferriki::{Highlighter, RemoteAssets, RenderOptions, StandardAssetCatalogs, render_html};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let assets = StandardAssetCatalogs::remote(RemoteAssets::default())?;
    let mut highlighter = Highlighter::builder()
        .with_assets(assets)
        .load_languages(["rust"])
        .load_themes(["github-dark"])
        .build()?;

    let tokens = highlighter.highlight(
        "fn main() { println!(\"Hello, Ferriki!\"); }",
        "rust",
        "github-dark",
    )?;
    let html = render_html(&tokens, &RenderOptions::default());
    println!("{html}");
    Ok(())
}

Run it with cargo run. The program prints a standalone <pre><code> HTML block with inline theme styles. This remote path needs no Ferriki checkout or assets/standard directory, and the crate runs without Node.js or an N-API runtime.

Keep highlighting

Reuse one Highlighter across code blocks and documents. It loads the requested languages and themes when you build it, then keeps those decoded assets in memory; the verified payloads also remain in the disk cache for later runs.

A highlighter is !Send: move the StandardAssetCatalogs to a worker thread and build one highlighter there. Custom grammars and themes register through LanguageRegistration and ThemeData, or from JSON with register_language_json and register_theme_json.

Keep the default regex prefilter for a CMS worker or a build that highlights many blocks. For a short-lived highlighter, add .with_regex_prefilter(false) to the builder to skip prefilter construction. This choice applies to loaded grammars, including embeddings and injections; it does not change matching or colors. The benchmark page shows the setup/reuse tradeoff measured through the Node API. Its percentages do not measure standalone Rust calls.

Run offline

After one run has filled the cache, set FERRIKI_ASSETS_REMOTE=0 to use cached payloads only. Keep the same FERRIKI_CACHE_DIR if you chose a custom cache path; a cache miss returns AssetUnavailable. FERRIKI_ASSETS_BASE_URL points at a mirror. The remote feature is unavailable on wasm32.

For a deployment that provisions assets itself, use a release-matched asset directory or embed the payloads. The Rust API guide details both options and release-digest verification.

With Ferromark

Ferromark, the Markdown parser of the same family, highlights fenced code with this crate natively: no Node.js process and no N-API boundary in between. Its optional ferriki feature provides FerrikiHighlightHooks for the HTML renderer and FerrikiJsxHooks for the JSX renderer.

To write your own hook, implement HtmlRenderHooks::highlight_code_block: it receives each fenced block's code and language. Ferriki highlights the lines; Ferromark keeps the wrapper, the fence metadata and the fallback for unknown languages.

use ferriki::{Highlighter, RenderOptions};
use ferromark::{CodeHighlightInput, HighlightedCodeBlock, HtmlRenderHooks};

struct FerrikiHook<'a> {
    highlighter: &'a mut Highlighter,
    theme: &'a str,
}

impl HtmlRenderHooks for FerrikiHook<'_> {
    fn highlight_code_block(
        &mut self,
        input: CodeHighlightInput<'_>,
    ) -> Option<HighlightedCodeBlock> {
        let language = input.language?.to_ascii_lowercase();
        let highlighted = self
            .highlighter
            .highlight_html_lines(input.code, &language, self.theme, &RenderOptions::default())
            .ok()?;
        Some(
            HighlightedCodeBlock::new(highlighted.lines)
                .with_colors(highlighted.foreground, highlighted.background),
        )
    }
}

Returning None makes Ferromark render escaped plain code. The full contract is in docs/rust-api.md; the API itself is on docs.rs/ferriki.

If you are comparing Rust HTML highlighters, see Ferriki and syntect. That comparison covers grammar and theme inputs, assets, language detection, output formats, and the limits of the current timing evidence. Ferriki currently renders HTML; it has no ANSI renderer.