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.