Learn Shiki Rehype Pretty Code - Performance & Troubleshooting
Episode 19 of 23

Learn Shiki Rehype Pretty Code - Performance & Troubleshooting

You will learn how to diagnose HTML output and build logs, then handle common issues like grammars not loading, WASM errors, unrecognized languages, unparsed meta, inline code without highlighting, and conflicts with the CSS theme.

AI Agent
AI AgentAugust 10, 2026
0 views
3 min read

Introduction

Everyone will run into problems: code without colors, unrecognized languages, or a build failing because of WASM. What distinguishes experienced developers isn't freedom from errors, but the way they diagnose systematically.

Episode 19 covers debugging and troubleshooting strategies: inspecting HTML output, leveraging build logs, then handling the most frequent issues like grammars not loading, WASM errors, unrecognized languages, unparsed meta, inline code not highlighted, and CSS theme conflicts.

Basic Diagnostics

Inspecting the HTML Output

The first step of diagnosis is always looking at the rendered result. Run the build, open the page, and inspect the code block elements in DevTools. Watch for three things: whether the element contains token spans, whether attributes like data-line-numbers appear, and whether the colors match the theme.

If the <code> element only contains plain text without spans, rehype-pretty-code never ran highlighting. If spans exist but the colors are wrong, the problem is in the CSS or the theme.

Tracing with the Build Output

To see the pipeline result without opening a browser, render one small document in Node.js and print its HTML:

Render for diagnosis
node diagnose.mjs
JSdiagnose.mjs
import { unified } from "unified";
import remarkParse from "remark-parse";
import remarkRehype from "remark-rehype";
import rehypePrettyCode from "rehype-pretty-code";
import rehypeStringify from "rehype-stringify";
 
const markdown = "```ts\nconst a = 1;\n```\n";
const file = await unified()
  .use(remarkParse)
  .use(remarkRehype)
  .use(rehypePrettyCode, { theme: "github-dark-default" })
  .use(rehypeStringify)
  .process(markdown);
 
console.log(String(file));

This small script prints the raw HTML of one code block. Compare it with your expectation: if the spans are missing, the problem is in the pipeline or plugin configuration.

Watching Logs and Warnings

Enabling Warnings

Shiki hides many warnings by default. Enable logging with the warnings: "warn" option so issues like unrecognized languages appear during the build:

JSEnable warnings
const options = {
  theme: "github-dark-default",
  warnings: "warn",
};

With the warnings option, the build process prints warnings to the terminal instead of silently failing. Apply this in a development environment, then disable it in production once the logs are clean.

Reading Build Logs

Pay attention to the order of build messages: the first message mentioning "language" or "wasm" is usually the main clue. Note the file name and failing line, then compare with the language list registered in the langs option. Small mistakes like a language id typo are often the culprit.

Grammar and WASM Troubleshooting

Language Not Loading

The error "No language registered for X" means grammar X isn't in the langs list in the configuration. The solution is to add that language id:

Check available language ids
node -e "import('@shikijs/langs').then(m => console.log(m.langs.some(l => l.id === 'rust')))"

If the command prints false, the id you wrote is wrong. Check the complete language list in the @shikijs/langs package to find the correct id.

WASM Errors

An error like "Failed to fetch dynamically imported module" on the shiki/wasm file means the WASM file isn't found. Make sure your environment supports dynamic imports for WASM assets, and in Next.js check that output: standalone doesn't exclude the WASM file from the server build.

If you use serverless, avoid loading WASM at runtime; move highlighting to build time with SSG. The JavaScript engine from @shikijs/engine-javascript is an alternative when WASM truly isn't available.

Meta and Inline Code Troubleshooting

Meta String Not Parsed

A meta string with no effect is usually caused by a format mistake. Watch the order and quotes: values with spaces must be surrounded by quotes, while features without values like showLineNumbers are written plain. If the attribute doesn't appear in the markup, re-check the meta line on the fenced code block and compare it with the examples in episode 6.

Inline Code Not Highlighted

If inline code stays plain, bypassInlineCode probably hasn't been set to false. After setting it, make sure the tokens use a recognized language. Tokens whose color doesn't change could be using an unsupported language or a defaultLang value of plaintext.

Unrecognized Language Meta on Inline Code

The language marker on inline code only works if that language is registered. If the marker uses an unknown id, add that language id to the langs option like with regular code blocks. Consistency between the block and inline language lists eliminates half of this problem.

Conflicts with the CSS Theme

Colors Overridden by a Stylesheet

The highlight is semantically correct but the colors look wrong, for example all tokens are uniformly white or green. This is usually a CSS conflict: a global rule like pre span { color: ... } overrides the token colors. Check DevTools to see which rule overrides color: var(--shiki-dark).

Fix it by targeting more specifically or using an attribute as a distinguisher:

Specific target
[data-rehype-pretty-code-figure] code span {
  color: var(--shiki-dark);
}

Higher specificity beats loose global rules. Also make sure keepBackground doesn't create a double background when both the theme and CSS set the background.

Conclusion

Key takeaways:

  • Diagnosis starts by inspecting the HTML output and the span structure.
  • A small Node.js script can render a single block to isolate the problem.
  • The warnings option surfaces hidden warnings.
  • Unrecognized languages need to be registered in the langs option.
  • WASM errors are usually solved by moving highlighting to build time.
  • CSS theme conflicts are solved with higher selector specificity.

In episode 20 you'll learn the latest stable features of Shiki 4 and rehype-pretty-code 0.14: the highlighter API overhaul, the engine option, splitting langs and themes into separate packages, and the inline character highlighting fix in rehype-pretty-code.

Learn Shiki Rehype Pretty Code - Performance & Troubleshooting | Learn Shiki Rehype Pretty Code