Documentation
Syntax Highlighting

Syntax Highlighting

Lodestone uses Shiki (opens in a new tab) to do syntax highlighting at build time. It's very reliable and performant. For example, adding this in your Markdown file:

Markdown
```js
console.log('hello, world')
```

Results in:

console.log('hello, world')

Features

Inlined Code

Inlined syntax highlighting like let x = 1 is also supported via the {:} syntax:

Markdown
Inlined syntax highlighting is also supported `let x = 1{:jsx}` via:

Highlighting Lines

You can highlight specific lines of code by adding a {} attribute to the code block:

Markdown
```js {1,4-5}
import { useState } from 'react'
 
function Counter() {
  const [count, setCount] = useState(0)
  return <button onClick={() => setCount(count + 1)}>{count}</button>
}
```

Result:

import { useState } from 'react'
 
function Counter() {
  const [count, setCount] = useState(0)
  return <button onClick={() => setCount(count + 1)}>{count}</button>
}

Highlighting Substrings

You can highlight specific substrings of code by adding a // attribute to the code block:

Markdown
```js /useState/
import { useState } from 'react'
 
function Counter() {
  const [count, setCount] = useState(0)
  return <button onClick={() => setCount(count + 1)}>{count}</button>
}
```
import { useState } from 'react'
 
function Counter() {
  const [count, setCount] = useState(0)
  return <button onClick={() => setCount(count + 1)}>{count}</button>
}

You can highlight only a part of the occurrences of that substring by adding a number it: /str/1, or multiple: /str/1-3, /str/1,3.

Copy Button

By adding a copy attribute, a copy button will be added to the code block when the user hovers over it:

Markdown
```js copy
console.log('hello, world')
```

Renders:

console.log('hello, world')

You can enable this feature globally by setting defaultShowCopyCode: true in your Lodestone configuration (next.config.js file). Once it's enabled globally, you can disable it via the copy=false attribute.

Line Numbers

You can add line numbers to your code blocks by adding a showLineNumbers attribute:

Markdown
```js showLineNumbers
import { useState } from 'react'
 
function Counter() {
  const [count, setCount] = useState(0)
  return <button onClick={() => setCount(count + 1)}>{count}</button>
}
```

Renders:

import { useState } from 'react'
 
function Counter() {
  const [count, setCount] = useState(0)
  return <button onClick={() => setCount(count + 1)}>{count}</button>
}

Filenames and Titles

You can add a filename or a title to your code blocks by adding a filename attribute:

Markdown
```js filename="example.js"
console.log('hello, world')
```

Renders:

example.js
console.log('hello, world')

ANSI Highlighting

You can highlight ANSI escape codes:

Markdown
```ansi
 ✓ src/index.test.ts (1)
   Test Files  1 passed (1)
        Tests  1 passed (1)
     Start at  23:32:41
     Duration  11ms
   PASS  Waiting for file changes...
         press h to show help, press q to quit
```

Renders:

  src/index.test.ts (1)
   Test Files  1 passed (1)
        Tests  1 passed (1)
     Start at  23:32:41
     Duration  11ms
   PASS  Waiting for file changes...
         press h to show help, press q to quit

Supported Languages

Check this list (opens in a new tab) for all supported languages.

Customize the Theme

Lodestone uses CSS variables to define the colors for tokens. You can inject a global CSS (opens in a new tab) to customize them under light/dark themes. For example this is the default tokens and you can override any of these:

styles.css
:root {
  --shiki-color-text: #414141;
  --shiki-color-background: transparent;
  --shiki-token-constant: #1976d2;
  --shiki-token-string: #22863a;
  --shiki-token-comment: #aaa;
  --shiki-token-keyword: #d32f2f;
  --shiki-token-parameter: #ff9800;
  --shiki-token-function: #6f42c1;
  --shiki-token-string-expression: #22863a;
  --shiki-token-punctuation: #212121;
  --shiki-token-link: #22863a;
}
 
.dark {
  --shiki-color-text: #d1d1d1;
  --shiki-token-constant: #79b8ff;
  --shiki-token-string: #ffab70;
  --shiki-token-comment: #6b737c;
  --shiki-token-keyword: #f97583;
  --shiki-token-parameter: #ff9800;
  --shiki-token-function: #b392f0;
  --shiki-token-string-expression: #4bb74a;
  --shiki-token-punctuation: #bbb;
  --shiki-token-link: #ffab70;
}

With Dynamic Content

Since syntax highlighting is done at build time, you can’t use dynamic content in your code blocks. However, since MDX is very powerful there is a workaround via client JS. For example:

dynamic_code.js
function hello () {
  const x = 2 + 3
  console.log(1)
}
Increase the numberChange to `1 + 1`

This workaround has a limitation that updated content won't be re-highlighted. For example if we update the number to 1 + 1, it will be incorrectly highlighted.

Check out the code (opens in a new tab) to see how it works.

Disable Syntax Highlighting

You can opt out of syntax highlighting for using one of your own. You can disable syntax highlighting globally by setting codeHighlight: false in your Lodestone configuration (next.config.js file).

OptionTypeDescription
codeHighlightbooleanEnable or disable syntax highlighting. Defaults to `true`.

Custom Grammar

Shiki accepts a VSCode TextMate Grammar (opens in a new tab) for syntax highlighting with custom language grammars.

You can provide these grammars by overriding the getHighlighter function in mdxOptions.rehypePrettyCodeOptions option in your Lodestone config inside next.config.js:

next.config.js
import { BUNDLED_LANGUAGES } from 'shiki'
 
Lodestone({
  // ... other options
  mdxOptions: {
    rehypePrettyCodeOptions: {
      getHighlighter: options =>
        getHighlighter({
          ...options,
          langs: [
            ...BUNDLED_LANGUAGES,
            // custom grammar options, see the Shiki documentation for how to provide these options
            {
              id: 'my-lang',
              scopeName: 'source.my-lang',
              aliases: ['mylang'], // Along with id, aliases will be included in the allowed names you can use when writing markdown.
              path: '../../public/syntax/grammar.tmLanguage.json'
            }
          ]
        })
    }
  }
})

Custom Themes

Within mdxOptions.rehypePrettyCodeOptions you may also provide custom themes instead of relying on CSS Variables:

next.config.js
Lodestone({
  // ... other options
  mdxOptions: {
    rehypePrettyCodeOptions: {
      // VSCode theme or built-in Shiki theme, see Shiki documentation for more information
      theme: JSON.parse(
        readFileSync('./public/syntax/arctis_light.json', 'utf8')
      )
    }
  }
})

Multiple Themes

Lodestone currently doesn't support specifying multiple themes. Because Shiki renders multiple code blocks for every theme and tags them with an attribute data-theme, e.g. data-theme="dark".

However, in the future, multiple themes will be supported. You can track the progress in shikiji (newly fork of Shiki) https://github.com/antfu/shikiji#multiple-themes (opens in a new tab) that already supports multiple themes without rendering multiple code blocks.