Skip to main content

Docusaurus + Shiki

This demo covers what is needed to get Shiki running as the code formatter instead of Prisma

Basic Installation

Install Dependencies

Install the Shiki Rehype plugin:

npm i @shikijs/rehype shiki

Add the plugin to your docusaurus config

You will need to add the plugin and its configwherever you have the docs and blog plugins enabled. This is typically through the default preset, but you may have more instances than this. It should be added to the beforeDefaultRehypePlugins array.

import rehypeShiki, { RehypeShikiOptions } from "@shikijs/rehype";
import { bundledLanguages, type BundledLanguage } from "shiki";

const config: Config = {
  // ...
  presets: [
    [
      "classic",
      {
        docs: {
          // ...
          beforeDefaultRehypePlugins: [ 
            [ 
              rehypeShiki,
              {
                theme: "catppuccin-latte",
                langs: ["js", "ts", "jsx", "tsx" /* other languages */],
                // alternatively, you can activate all bundled languages:
                // langs: Object.keys(bundledLanguages) as BundledLanguage[]
              } satisfies RehypeShikiOptions,
            ],
          ],
        },
        blog: {
          // same as above
        },
      },
    ],
  ],
  // ...
};

Dark/Light Theme

By default, Shiki only uses 1 theme but makes it simple to enable a dark/light theme.

Adjust the plugin config to enable light/dark themes

[
  rehypeShiki,
  {
    theme: "catppuccin-latte",
    themes: {
      light: "catppuccin-latte",
      dark: "catppuccin-macchiato"
    },
    langs: Object.keys(bundledLanguages) as BundledLanguage[]
  } satisfies RehypeShikiOptions,
],

Add the CSS to your site

Shiki uses css variables attached to each element to swap themes. You will need to add some styles to your site CSS so that they toggle when switching the docusaurus theme.

[data-theme="dark"] pre {
  color: var(--shiki-dark) !important;
  background-color: var(--shiki-dark-bg) !important;
}
[data-theme="dark"] pre span {
  color: var(--shiki-dark) !important;
}

Swizzle some components

Since @shiki/rehype will handle parsing the code from the markdown, we don't need to replace the MDXComponents with the @theme versions. This means we need to swizzle the Code and Pre components to just return their original elements:

Code Component

Swizzle the component:

npx docusaurus swizzle @docusaurus/theme-classic MDXComponents/Code --typescript --eject

Edit the file src/theme/MDXComponents/Code.tsx

import type { ComponentProps } from "react";
import React from "react";
import CodeInline from "@theme/CodeInline";
import type { Props } from "@theme/MDXComponents/Code";

function shouldBeInline(props: Props) {
  return (
    // empty code blocks have no props.children,
    // see https://github.com/facebook/docusaurus/pull/9704
    typeof props.children !== "undefined" &&
    React.Children.toArray(props.children).every(
      (el) => typeof el === "string" && !el.includes("\n")
    )
  );
}

function CodeBlock(props: ComponentProps<"code">): JSX.Element {
  return <code {...props} />;
}

export default function MDXCode(props): JSX.Element {
  return shouldBeInline(props) ? (
    <CodeInline {...props} />
  ) : (
    <CodeBlock {...props} />
  );
}

Pre Component

Swizzle the component:

npx docusaurus swizzle @docusaurus/theme-classic MDXComponents/Pre --typescript --eject

Edit the file src/theme/MDXComponents/Pre.tsx:

import React, { type ReactNode } from "react";
import type { Props } from "@theme/MDXComponents/Pre";

export default function MDXPre(props: Props): ReactNode | undefined {
  return <pre {...props} />;
}

Conclusion

This example is already using Shiki. You can refer to the docusaurus.config.ts config in the site source repo for a more complete example including shiki transformers and related styles.