You will learn to use the @shikijs/transformers package to add features like diff and highlight via line notation, and create a custom transformer to change tokens and metadata of the highlighting result according to project needs.

Meta strings give you control through Markdown syntax, but not every need can be expressed through meta. When you want to manipulate tokens, mark lines with special notation, or add metadata to the output, it's transformers' turn to work.
Episode 10 covers transformers in the Shiki ecosystem: the basic transformer concept, the ready-to-use transformer catalog from @shikijs/transformers, how to use them via the transformers option on rehype-pretty-code, and how to create your own custom transformer. Transformers are the key to breaking past the limits of built-in features.
Shiki processes code in several stages: tokenization, transformation, then rendering to HAST. A transformer is a function that inserts logic between those stages. Through hooks like preprocess, code, line, and span, you can modify the code, tokens, and elements before the final HTML is formed.
Each transformer is an object carrying a name property and a set of hooks. Hooks receive the data of a certain stage and may return new values to pass on to the next stage.
Transformers have been separated into their own package since Shiki v4. Install it alongside shiki:
npm i shiki @shikijs/transformersThe npm i shiki @shikijs/transformers command adds both dependencies to the project. The @shikijs/transformers package contains a collection of ready-to-use transformers written and tested by the Shiki team.
This group of transformers reads special comments inside the code as visual instructions. The most commonly used examples:
transformerNotationDiff recognizes // [!code ++] and // [!code --] comments to mark added and removed lines.transformerNotationHighlight recognizes // [!code highlight] to highlight lines.transformerNotationFocus recognizes // [!code focus] to focus lines.transformerNotationWordHighlight marks words after // [!code word:...].This notation is popular because authors only need to write a comment on the original code line, without an extra meta string. It suits content written in the VitePress style.
Besides notation, there are transformers for technical needs:
transformerRenderWhitespace visually displays spaces and tabs.transformerCompactLineOptions tidies line options in the output.transformerMetaHighlight translates meta strings into line highlighting.transformerRemoveLineBreak removes unwanted empty lines.Only use the transformers you really need, because each transformer adds work at render time.
rehype-pretty-code forwards the transformers option to Shiki. Arrange the transformer list in the plugin configuration:
import {
transformerNotationDiff,
transformerNotationHighlight,
} from "@shikijs/transformers";
const options = {
theme: "github-dark-default",
transformers: [
transformerNotationDiff(),
transformerNotationHighlight(),
],
};With this configuration, you can write // [!code ++] comments inside a code block and that line is automatically marked as added. The order in the array determines the execution order of the hooks.
Here's a TypeScript block using diff notation via a transformer:
const lama = 1;
const baru = 2;
console.log(lama);
console.log(baru);Notice that you don't need to write the diff meta string on the opening line. The notation transformer reads comments inside the code content independently. This is the advantage of using transformers: features are moved from the meta string into the code content.
You can create your own transformer as a ShikiTransformer object with hooks. The following example adds a data-theme-source attribute to the code block element:
import type { ShikiTransformer } from "shiki";
export const themeSourceTransformer: ShikiTransformer = {
name: "theme-source-transformer",
preprocess(code, options) {
options.theme = options.theme ?? "github-dark-default";
return code;
},
code(hast) {
hast.properties["data-theme-source"] = "shiki";
},
};The preprocess hook modifies the input before tokenization, while the code hook receives the <pre> HAST node and may modify its properties. The combination of the two covers most customization needs.
Choose a hook according to the stage you want to change:
preprocess to modify the code string before tokenization.tokens to change the set of already-annotated tokens.line to modify the HAST element of a single line.span to modify the HAST element of a single token.code to modify the HAST element of the whole code block.Each hook receives that stage's data and can return new data. You can also use a dev version of shiki to test a custom transformer before using it in production.
Key takeaways:
@shikijs/transformers package provides ready-to-use transformers.// [!code ++] enriches code without meta strings.transformers option in rehype-pretty-code accepts an array of transformers.name property and hooks.preprocess, line, span, and code hooks control every output layer.In episode 11 you'll learn integration with content frameworks: installing the same pipeline in Next.js with MDX or Velite, plus integration patterns in Astro, SvelteKit, and VitePress, each differing only at the configuration point.