Benefits of Using Shiki for Code Highlighting in Astro: Complete Technical Guide

Shiki delivers VS Code-grade syntax highlighting with zero runtime overhead by generating static HTML at build time.

The 30-seconds-of-code project leverages Shiki for code highlighting in Astro to produce high-fidelity syntax highlighting without burdening the client. Unlike traditional client-side highlighters, this approach pre-renders all code blocks during the static site generation process. The result is a faster, more accurate presentation of code snippets that matches the visual experience developers expect from modern editors.

Architecture: How Shiki Integrates with Astro

The implementation follows a pipeline that converts raw markdown into static HTML before reaching the browser.

The process begins in src/lib/contentUtils/extractor.js, where the extractData function selects Shiki as the default highlighter. It invokes ShikiHighlighter.setup with available language grammars, creating a singleton highlighter instance. This instance utilizes the Oniguruma WASM engine for tokenization and loads bundled language definitions from @shikijs/langs/${lang}.

Once initialized, the MarkdownParser processes code fences through ShikiHighlighter.highlightCode, generating HTML strings through the codeToHtml method. These pre-rendered strings become part of each Snippet model. Finally, AstroContent.generateSnippetPages() writes these snippets to JSON files that Astro consumes at build time, ensuring the final pages contain fully highlighted code without client-side execution.

Core Benefits of Shiki for Static Sites

VS Code-Level Syntax Accuracy

Shiki reuses the same TextMate grammars that power VS Code, delivering tokenization precision that matches the editor developers use daily. In src/lib/contentUtils/markdownParser/codeHighlighters/shiki.js, languages load dynamically via loadBundledLanguages, which constructs a lazy import map from the grammars list. This ensures the site supports exact syntax definitions rather than simplified approximations.

WASM-Driven Performance

The heavy parsing logic runs inside a compact WebAssembly module, keeping client-side bundles minimal. The highlighter configuration specifies engine: () => createOnigurumaEngine(import('shiki/wasm')), ensuring textmate parsing happens server-side during the build. Since highlighting occurs at build time, the runtime cost for users is zero.

Bundled Themes and Custom Transformers

The project ships with a custom "cosmos" theme and enhances visual presentation through custom transformers. The highlightCode method applies transformerColorSwatches, transformerLineHighlights, transformerSectionFolding, and transformerColorizedBrackets to add interactive features without JavaScript execution. Themes load via loadBundledThemes in shiki.js, while transformers execute during the HTML generation phase.

Static-Site Friendliness

Because Shiki returns pure HTML strings through codeToHtml, Astro writes the final markup directly to the output folder via AstroContent.generateSnippetPages(). The snippet.page.serialize objects contain pre-highlighted HTML that Astro injects into pages, eliminating the need for client-side highlighting libraries entirely.

Implementation Examples

Setting Up the Shiki Highlighter

The ShikiHighlighter class initializes the singleton instance with bundled languages and the WASM engine:

// src/lib/contentUtils/markdownParser/codeHighlighters/shiki.js
export default class ShikiHighlighter {
  static setup(grammars) {
    const bundledLanguages = loadBundledLanguages(grammars);
    const bundledThemes = loadBundledThemes();

    const createHighlighter = createdBundledHighlighter({
      langs: bundledLanguages,
      themes: bundledThemes,
      engine: () => createOnigurumaEngine(import('shiki/wasm')),
    });

    Object.assign(this, createSingletonShorthands(createHighlighter));
  }
}

The setup method receives the grammars list from extractData and configures the highlighter for the markdown parser.

Selecting the Highlighter

The extraction layer allows switching between highlighters while defaulting to Shiki:

// src/lib/contentUtils/extractor.js
export const extractData = async (highlighter = 'shiki') => {
  const codeHighlighter =
    highlighter === 'shiki' ? ShikiHighlighter : PrismHighlighter;
  codeHighlighter.setup(grammars);

  MarkdownParser.setupProcessors({ languages, grammars, codeHighlighter });
};

Changing the default parameter to "prism" would revert to the legacy highlighter, though this bypasses the performance benefits of the WASM-based approach.

Generating Highlighted HTML

The highlightCode method applies the cosmos theme and custom transformers:

// Inside ShikiHighlighter.highlightCode()
const highlightedCode = await this.codeToHtml(code, {
  lang: language,
  theme: 'cosmos',
  transformers: [
    transformerColorSwatches(),
    transformerLineHighlights(metadata),
    transformerSectionFolding(metadata),
    transformerColorizedBrackets({ themes: { cosmos: [...] } }),
  ],
});

Custom transformers add visual enhancements like bracket colorization and collapsible sections directly into the static HTML.

Static Generation Integration

Astro receives pre-rendered content through the content generation pipeline:

// src/lib/astroContent.js
static generateSnippetPages() {
  const snippets = process.env.NODE_ENV === 'development'
    ? Snippet.all
    : Snippet.scope('published');

  const pages = snippets.reduce((acc, snippet) => {
    acc[snippet.page.key] = snippet.page.serialize;
    return acc;
  }, {});

  fs.writeJson(settings.paths.out.snippets, pages, ...this.outputParams);
}

The page.serialize property contains the fully highlighted HTML, allowing Astro to write static files without runtime highlighting overhead.

Summary

  • Shiki provides VS Code-grade accuracy by using the same TextMate grammars as the popular editor, loaded dynamically from @shikijs/langs bundles.
  • Zero runtime overhead results from executing the Oniguruma WASM engine during the build process, with AstroContent.generateSnippetPages() writing pre-highlighted HTML to static files.
  • Custom visual features like line highlights and section folding are implemented through server-side transformers in shiki.js, requiring no client-side JavaScript.
  • Architecture flexibility allows fallback to PrismJS via the extractData parameter, though Shiki remains the default for its superior static generation capabilities.

Frequently Asked Questions

How does Shiki differ from PrismJS in Astro projects?

Shiki generates highlighted HTML at build time using a WASM-based TextMate parser, while PrismJS traditionally runs in the browser. The 30-seconds-of-code implementation stores pre-highlighted strings in snippet.page.serialize, eliminating client-side execution and reducing bundle sizes. Shiki also provides more accurate syntax tokenization by using VS Code's exact grammar definitions rather than simplified regex patterns.

Can I use custom themes with Shiki in Astro?

Yes, the 30-seconds-of-code repository demonstrates this by loading a custom "cosmos" theme through loadBundledThemes() in src/lib/contentUtils/markdownParser/codeHighlighters/shiki.js. You can drop JSON theme files into the themes directory and reference them by name in the codeToHtml configuration. The setup supports both bundled themes from Shiki's collection and completely custom color schemes.

Does Shiki impact the build performance of large Astro sites?

The initial setup in extractData creates a singleton highlighter that reuses the WASM engine across all files, minimizing overhead. While the TextMate parsing is computationally heavier than simple regex highlighting, it runs entirely during the build phase. The resulting static HTML requires no client-side processing, making the runtime performance significantly faster than client-side alternatives.

What languages does Shiki support in this implementation?

The implementation uses loadBundledLanguages to dynamically import language grammars from @shikijs/langs/${lang} based on a configurable grammars list. This approach supports all languages available in the Shiki ecosystem while keeping the initial bundle size small through lazy loading. New languages can be added by updating the grammars configuration and ensuring the corresponding Shiki language package is available.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →