rehype plugin to highlight code blocks in HTML with Prism (via refractor) with additional line highlighting and line numbers functionalities.
Inspired by and uses a compatible API as @mapbox/rehype-prism with additional support for line-highlighting, line numbers and diff code blocks.
Tested to work with xdm and mdx v2 libraries such as mdx-bundler. If you are using mdx v1 libraries such as next-mdx-remote, you will need to patch it with the fixMetaPlugin
discussed in this issue, before rehype-prism-plus
.
An appropriate stylesheet should be loaded to style the language tokens, format line numbers and highlight lines. You can specify language for diff code blocks by using diff-[language] to enable syntax highlighting in diffs.
This package is ESM only:
Node 12+ is needed to use it and it must be import
ed instead of require
d.
npm install rehype-prism-plus
The following import paths are supported:
rehype-prism-plus/generator
, generator function. Can be used to generate a rehype prism plugin that works on your desired languages.rehype-prism-plus/common
, rehype plugin. Supports the languages inrefractor/lib/common.js
.rehype-prism-plus/all
, rehype plugin. Works with all language supported by refractor.rehype-prism-plus
, re-exports the above 3 packages withrehype-prism-plus/all
as the default export.
Some examples of how you might use the rehype plugin:
import rehype from 'rehype'
import rehypePrism from 'rehype-prism-plus'
rehype().use(rehypePrism).process(/* some html */)
Here's an example of syntax highlighting in Markdown, with xdm
import { compile } from 'xdm'
import rehypePrism from 'rehype-prism-plus'
async function main(code) {
console.log(String(await compile(code, { rehypePlugins: [rehypePrism] })))
}
main(`~~~js
console.log(1)
~~~`)
Input:
```js {1,3-4} showLineNumbers
function fancyAlert(arg) {
if (arg) {
$.facebox({ div: '#foo' })
}
}
```
HTML Output:
<code class="language-js">
<div class="code-line line-number highlight-line" line="1">
<span class="keyword">function</span>
<span class="function">fancyAlert</span><span class="punctuation">(</span
><span class="">arg</span><span class="punctuation">)</span>
<span class="punctuation">{</span>
</div>
<div class="code-line line-number highlight-line" line="2">
<span class="keyword">if</span>
<span class="punctuation">(</span>arg<span class="punctuation">)</span>
<span class="punctuation">{</span>
</div>
<div class="code-line line-number" line="3">
$<span class="punctuation">.</span><span class="function">facebox</span
><span class="punctuation">(</span><span class="punctuation">{</span> div<span class="">:</span>
<span class="string">'#foo'</span>
<span class="punctuation">}</span><span class="punctuation">)</span>
</div>
<div class="code-line line-number" line="4">
<span class="punctuation">}</span>
</div>
<div class="code-line line-number" line="5">
<span class="punctuation">}</span>
</div></code
>
To customise the languages for your own prism plugin:
import { refractor } from 'refractor/lib/core.js'
import markdown from 'refractor/lang/markdown.js'
import rehypePrismGenerator from 'rehype-prism-plus/generator'
refractor.register(markdown)
const myPrismPlugin = rehypePrismGenerator(refractor)
To style the language tokens, you can just copy them from any prismjs compatible ones. Here's a list of themes.
In addition, the following styles should be added for line highlighting and line numbers to work correctly:
pre {
overflow-x: auto;
}
/**
* Inspired by gatsby remark prism - https://www.gatsbyjs.com/plugins/gatsby-remark-prismjs/
* 1. Make the element just wide enough to fit its content.
* 2. Always fill the visible space in .code-highlight.
*/
.code-highlight {
float: left; /* 1 */
min-width: 100%; /* 2 */
}
.code-line {
display: block;
padding-left: 16px;
padding-right: 16px;
margin-left: -16px;
margin-right: -16px;
border-left: 4px solid rgba(0, 0, 0, 0); /* Set placeholder for highlight accent border color to transparent */
}
.code-line.inserted {
background-color: rgba(16, 185, 129, 0.2); /* Set inserted line (+) color */
}
.code-line.deleted {
background-color: rgba(239, 68, 68, 0.2); /* Set deleted line (-) color */
}
.highlight-line {
margin-left: -16px;
margin-right: -16px;
background-color: rgba(55, 65, 81, 0.5); /* Set highlight bg color */
border-left: 4px solid rgb(59, 130, 246); /* Set highlight accent border color */
}
.line-number::before {
display: inline-block;
width: 1rem;
text-align: right;
margin-right: 16px;
margin-left: -8px;
color: rgb(156, 163, 175); /* Line number color */
content: attr(line);
}
Here's the styled output using the prism-night-owl theme:
For more information on styling of language tokens, consult refractor and Prism.
rehype().use(rehypePrism, [options])
Syntax highlights pre > code
.
Under the hood, it uses refractor, which is a virtual version of Prism.
The code language is configured by setting a language-{name}
class on the <code>
element.
You can use any language supported by refractor.
If no language-{name}
class is found on a <code>
element, it will be skipped.
Type: boolean
.
Default: false
.
By default, if {name}
does not correspond to a language supported by refractor an error will be thrown.
If you would like to silently skip <code>
elements with invalid languages or support line numbers and line highlighting for code blocks without a specified language, set this option to true
.
Type: string
.
Default: ``.
Uses the specified language as the default if none is specified. Takes precedence over ignoreMissing
.
Note: The language must be first registered with refractor.
Type: boolean
.
Default: false
.
By default, line numbers will only be displayed for code block cells with a meta property that includes 'showLineNumbers'. To control the starting line number use showLineNumbers=X
, where X
is the starting line number as a meta property for the code block.
If you would like to show line numbers for all code blocks, without specifying the meta property, set this to true
.
Note: This will wrongly assign a language class and the class might appear as language-{1,3}
or language-showLineNumbers
, but allow the language highlighting and line number function to work. An possible approach would be to add a placeholder like unknown
so the div
will have class="language-unknown"