Line Anchors

Add unique IDs to each line in code blocks, enabling URL-based navigation to specific lines with visual highlighting.

ShikiMarkdownMDX

Preview

Code block with 'example' prefix
const code = `console.log('Hello World')`;
const highlighter = await highlight();
const html = highlighter.codeToHtml(code, {
  lang: "javascript",
  transformers: [lineAnchors({ addPrefix: "example" })],
});
Output HTML
<pre>
  <code>
    <span class="line" id="example-l1" data-line="1">
      <!--...-->
    </span>
    <span class="line" id="example-l2" data-line="2">
      <!--...-->
    </span>
    <span class="line" id="example-l3" data-line="3">
      <!--...-->
    </span>
  </code>
</pre>
Highlight line 5 of the code block

Installation

shadcn/ui

shadcn/ui Command
pnpm dlx shadcn@latest add https://code-blocks.pheralb.dev/r/shiki-line-anchors.json

Manual

  1. Copy the lineAnchors transformer into your project:
.ts
import type { ShikiTransformer } from "shiki";

interface LineAnchorsOptions {
  addPrefix?: string;
}

const lineAnchors = ({ addPrefix }: LineAnchorsOptions = {}): ShikiTransformer => {
  return {
    name: "LineAnchors",
    line(node, line) {
      const rawMeta = this.options.meta?.__raw;
      if (!rawMeta) return;
      const prefix = rawMeta.match(/prefix=(["'])(.*?)\1/)?.[2] ?? addPrefix;
      if (!prefix) return;
      node.properties.id = `${prefix}-l${line}`;
      node.properties["data-line"] = line;
    },
    pre(node) {
      const existingClass = node.properties.class;
      if (Array.isArray(existingClass)) {
        existingClass.push("shiki-line-anchors");
      } else if (typeof existingClass === "string") {
        node.properties.class = `${existingClass} shiki-line-anchors`;
      } else {
        node.properties.class = "shiki-line-anchors";
      }
    },
  };
};

export { lineAnchors };
  1. shiki-line-anchors styles are defined in the shiki.css file:
shikiShiki Highlighter#styles

The main highlighter utility to render syntax highlighted code.

Usage

shikijs/rehype

  1. Import the transformer in your rehypeShiki plugin:
Rehype Shiki Options
import { lineAnchors } from "@/utils/shiki/transformers/line-anchors";

const rehypeShikiOptions: RehypeShikiCoreOptions = {
  //...
  transformers: [lineAnchors()],
};

export { rehypeShikiOptions };
  1. In your Markdown or MDX files add the prefix option to your code blocks:
.mdx
```javascript prefix="example"
console.log("Hello World");
```

highlight

Import the transformer in your highlight() function with addPrefix property:

Using lineAnchors with highlighter
import { highlight } from "@/utils/shiki";
import { lineAnchors } from "@/utils/shiki/transformers/line-anchors";

const code = `console.log('hello')`;
const highlighter = await highlight();
const html = highlighter.codeToHtml(code, {
  //...
  transformers: [lineAnchors({ addPrefix: "example" })],
});