Code Block
Syntax-highlighted code blocks powered by rehype-pretty-code and Shiki
Installation
npx @ravikumarsurya/mdx-ui add code-blockThen set up syntax highlighting with rehype-pretty-code:
npm install rehype-pretty-code shikiSetup
Next.js (next.config.ts)
import createMDX from "@next/mdx";
import rehypePrettyCode from "rehype-pretty-code";
const withMDX = createMDX({
options: {
rehypePlugins: [
[
rehypePrettyCode,
{
theme: { light: "github-light-default", dark: "github-dark-default" },
defaultColor: false,
},
],
],
},
});
export default withMDX({ pageExtensions: ["ts", "tsx", "md", "mdx"] });next-mdx-remote
import { compileMDX } from "next-mdx-remote/rsc";
import rehypePrettyCode from "rehype-pretty-code";
const { content } = await compileMDX({
source,
options: {
mdxOptions: {
rehypePlugins: [
[
rehypePrettyCode,
{
theme: {
light: "github-light-default",
dark: "github-dark-default",
},
defaultColor: false,
},
],
],
},
},
});Contentlayer / Contentlayer2
// contentlayer.config.ts
import rehypePrettyCode from "rehype-pretty-code";
export default makeSource({
mdxOptions: {
rehypePlugins: [
[
rehypePrettyCode,
{
theme: { light: "github-light-default", dark: "github-dark-default" },
defaultColor: false,
},
],
],
},
});Map pre to CodeBlock
In your MDX components file, map the pre element to CodeBlock:
import { CodeBlock } from "@/components/mdx/code-block";
const components = {
pre: CodeBlock,
// ... other components
};Usage
import { Badge } from "@/components/mdx/badge"
export function StatusBadge({ status }: { status: string }) {
return <Badge variant="success">{status}</Badge>
}Once configured, fenced code blocks in MDX get full syntax highlighting automatically:
```tsx title="button.tsx"
export function Button({ children }: { children: React.ReactNode }) {
return (
<button className="px-4 py-2 bg-primary text-primary-foreground rounded-md">
{children}
</button>
);
}
```With a title
Annotate the code fence with title="...":
```bash title="Install dependencies"
npm install rehype-pretty-code shiki
```Line highlighting
rehype-pretty-code supports line and word highlighting out of the box:
```tsx {2,4-6}
function greet(name: string) {
const greeting = `Hello, ${name}!`; // highlighted
console.log(greeting);
return {
// highlighted
message: greeting, // highlighted
timestamp: Date.now(), // highlighted
};
}
```Word highlighting
```tsx /useState/
const [count, setCount] = useState(0);
const [name, setName] = useState("");
```How it works
The CodeBlock component renders as the <pre> element in MDX. When rehype-pretty-code processes your MDX:
- It reads the language and title from your code fence annotation
- Runs Shiki to tokenize and highlight the code
- Passes
data-languageanddata-titleto the<pre>element CodeBlockreads those attributes to show the language badge and title
The CSS for Shiki token colors is automatically injected into your globals.css by npx @ravikumarsurya/mdx-ui init:
/* Shiki dual-theme — injected by mdx-ui init */
[data-code-block] code span {
color: var(--shiki-light);
font-style: var(--shiki-light-font-style);
font-weight: var(--shiki-light-font-weight);
}
.dark [data-code-block] code span {
color: var(--shiki-dark);
font-style: var(--shiki-dark-font-style);
font-weight: var(--shiki-dark-font-weight);
}Props
| Prop | Type | Default | Description |
|---|---|---|---|
title | string | - | Override the title shown in the header |
showLineNumbers | boolean | false | Enable CSS counter-based line numbers |
data-language | string | - | Set by rehype-pretty-code; shown as language badge |
data-title | string | - | Set by rehype-pretty-code from title annotation |
className | string | - | Additional classes on the <pre> element |
Without rehype-pretty-code
The component still works without a syntax highlighting plugin — code renders with the correct monospace font and copy button, just without token colors.