Numbered Code Block Lines in Eleventy with Shiki Twoslash

Last Updated

Bringing in a third-party library for easy, reliable line numbering
A bull, with parts of the body indicated and numbered: the numbers forming a border around the image. Woodcut, 17--
A bull, with parts of the body indicated and numbered: the numbers forming a border around the image. Woodcut, 17--. Public Domain Mark. Source: Wellcome Collection.

Markdown Code Block Styling, Part 1

This article is part of a series on styling markdown code blocks.

This first installment covers numbering lines. The second will cover highlighting lines. The third will cover added lines, deleted lines, and focused lines, in combination with numbered and/or highlighted lines.

The second, Highlight Code Block Lines In Eleventy with Shiki Twoslash, covers line highlighting.

The third will cover styling added lines, deleted lines, and focused lines, in combination with numbered and/or highlighted lines.

There’s no way to number lines in the code block HTML generated by Eleventy’s default Markdown library, markdown-it, which turns Markdown like this

md
md
```md
first line
second
```
md
```md
first line
second
```

into HTML like this

html
html
<pre><code class="language-md">first line
second
</code></pre>
html
<pre><code class="language-md">first line
second
</code></pre>

The Prism-based first party syntax highlighting plugin, eleventy-plugin-syntaxhighlight, doesn’t pick up on Prism’s line number support. You can try to hack it by misusing Prism’s line highlighting, setting eleventy-plugin-syntaxhighlight’s undocumented alwaysWrapLineHighlights option to true, but ⚠️ there are multiple reports of that being glitchy — 11ty/eleventy-plugin-syntaxhighlight#10.

A solution

remark-shiki-twoslash, on the other hand, wraps each code block line in a div. For Eleventy v2 there’s the remark-shiki-twoslash plugin eleventy-plugin-shiki-twoslash. For Eleventy v3 you can use remark-shiki-twoslash with a small wrapper in your .eleventy.js file; see shikijs/twoslash#193 for details.

Shiki Twoslash, not to be confused with Shiki Twoslash

As of this writing, there are two Shiki Twoslashes, one under the shikijs org and one under the twoslashes org. remark-shiki-twoslash uses shikijs/twoslash.

Not just for Eleventy

There are remark-shiki-twoslash plugins for Markdown-It, Docusaurus, Eleventy, Gatsby, Hexo, and VuePress. Learn more at https://github.com/shikijs/twoslash.

This Markdown

md
md
```md
first line
second
```
md
```md
first line
second
```

becomes markup similar to this

html
html
<pre class="shiki">
<div class="language-id">markdown</div>
<div class="code-container">
<code>
<div class="line">first line</div>
<div class="line">second</div>
</code>
</div>
</pre>
html
<pre class="shiki">
<div class="language-id">markdown</div>
<div class="code-container">
<code>
<div class="line">first line</div>
<div class="line">second</div>
</code>
</div>
</pre>

A CSS counter can be used to style those .lines. Something like

css
css
pre.shiki {
counter-reset: codeblock-line;
.line {
counter-increment: codeblock-line;
&::before {
content: counter(codeblock-line);
}
}
}
css
pre.shiki {
counter-reset: codeblock-line;
.line {
counter-increment: codeblock-line;
&::before {
content: counter(codeblock-line);
}
}
}

Opting out of line numbers

With remark-shiki-twoslash, classes can be added to code blocks by including class attribute markup on the Markdown code block’s opening fence if a language is also specified on the opening fence.

This

md
md
```md
```md class="my-class"
md
```md
```md class="my-class"

renders as

html
html
<pre class="shiki">
<pre class="shiki my-class">
html
<pre class="shiki">
<pre class="shiki my-class">

Use that to support opting out of line numbers:

css
css
pre.shiki {
pre.shiki:not(.codeblock-no-line-numbers) {
/* … */
}
css
pre.shiki {
pre.shiki:not(.codeblock-no-line-numbers) {
/* … */
}
Markdown Source
md
md
```text
has
line
numbers
```
md
```text
has
line
numbers
```



Rendered Result

text
has
line
numbers
text
has
line
numbers
Markdown Source
md
md
```text class="codeblock-no-line-numbers"
doesn't
have
line
numbers
```
md
```text class="codeblock-no-line-numbers"
doesn't
have
line
numbers
```



Rendered Result

text
doesn't
have
line
numbers
text
doesn't
have
line
numbers

Articles You Might Enjoy

Or Go To All Articles