MDX Notes
Everything about showing code in MDX — inline code, fenced blocks, syntax highlighting, line numbers, file names, copy buttons, live editors, and diffs.
If you're writing technical docs, code blocks are probably 50% of your content. MDX handles everything from a quick inline snippet to full interactive editors where readers can modify and run code.
Here's every technique you'll need.
Inline Code
Wrap something in single backticks to make it look like code within a sentence:
Use the `useState` hook to manage component state.
Run `npm install` to install dependencies.
The `className` prop accepts CSS class names.Use the useState hook to manage component state.
When to Use Inline Code
Use it for anything that's "code" in the middle of regular text:
- Function names:
useState,useEffect - Variable names:
isLoading,userData - File names:
package.json,tsconfig.json - Terminal commands:
npm run build - CSS properties:
display: flex - HTML elements:
<div>,<section> - Keyboard shortcuts in technical context:
Ctrl+C
Backticks Inside Inline Code
If your code itself contains a backtick, wrap it in double backticks with spaces:
`` `template literal` ``
``const str = `Hello ${name}` ``Custom Inline Code Styling
You can override how inline code renders:
Fenced Code Blocks
For multi-line code, use triple backticks:
function hello() { console.log('Hello, World!') }
That gives you a preformatted block with monospace font.
Alternative: Tilde Fence
Tildes (~~~) work too, if you prefer:
~~~
function hello() {
console.log('Hello, World!')
}
~~~Language Specification
Add the language name right after the opening fence. This turns on syntax highlighting:
function greet(name) { return Hello, ${name}! }
Common Language Identifiers
Here are the ones you'll use most:
const x = 42
const x: number = 42
function App() { return <div>Hello</div> }
function App(): JSX.Element { return <div>Hello</div> }
def greet(name): return f"Hello, {name}"
npm install @mdx-js/react
.container { display: flex; gap: 1rem; }
<div class="container"> <p>Hello World</p> </div>
{ "name": "my-project", "version": "1.0.0" }
name: Build on: push: branches: [main]
SELECT users.name, orders.total FROM users INNER JOIN orders ON users.id = orders.user_id WHERE orders.total > 100;
Title
- List item
- Another item
import { Button } from './Button'
Hello
Click me
Line Highlighting
Want to draw attention to specific lines? Add line numbers in curly braces after the language:
import React from 'react'
// This line is highlighted function Counter() { const [count, setCount] = React.useState(0)
// These lines are highlighted const increment = () => { setCount(prev => prev + 1) }
return <button onClick={increment}>{count}</button> }
Different Highlighting Syntax
Depending on your setup, you might use different approaches:
{/* Specific lines */}{/* Range of lines */}
{/* Mixed lines and ranges */}
{/* Using comments (some frameworks) */}
const normal = true // highlight-next-line const highlighted = true const alsoNormal = true
{/* Highlight block (Docusaurus-style) */}
// highlight-start const a = 1 const b = 2 const c = 3 // highlight-end
Addition and Deletion Highlighting
function App() { return ( <OldComponent /> <NewComponent /> ) }
Line Numbers
Show line numbers next to code:
{/* Meta string approach */}function fibonacci(n) { if (n <= 1) return n return fibonacci(n - 1) + fibonacci(n - 2) }
console.log(fibonacci(10))
{/* Starting from a specific number */}
// This continues from line 15 const result = processData(input) return result.map(item => item.value) }
Line Numbers + Highlighting Together
import { useState } from 'react'
function useToggle(initial = false) { const [value, setValue] = useState(initial) const toggle = () => setValue(v => !v) return [value, toggle] as const }
File Names / Titles
Tell readers which file this code goes in. Use title= in the meta string:
{/* Title in meta string */}export function Button({ children, onClick }) { return ( <button className="btn" onClick={onClick}> {children} </button> ) }
{ "name": "my-mdx-project", "dependencies": { "@mdx-js/react": "^3.0.0", "next": "^14.0.0" } }
npm create next-app@latest my-project cd my-project npm run dev
File Tab Component
When you need to show multiple related files together:
import { CodeTabs } from '../components/CodeTabs'
<CodeTabs>
<CodeTabs.Tab title="Button.jsx">export function Button({ children }) { return <button className="btn">{children}</button> } ``` </CodeTabs.Tab> <CodeTabs.Tab title="Button.module.css">
export { Button } from './Button'
</CodeTabs.Tab>
</CodeTabs>Copy Button
Readers want to copy your code. Make it easy. Here's a component approach:
Component-Based Approach
import { CodeBlock } from '../components/CodeBlock'
<CodeBlock language="bash" copyButton>
npm install @mdx-js/react @mdx-js/loader
</CodeBlock>Implementation
Make Every Code Block Copyable Automatically
Override the pre element so all fenced code blocks get a copy button without any extra work:
Now every fenced code block in your MDX gets a copy button. No changes to existing content needed.
Live Code Editors
This is where MDX really shines. You can let readers edit code and see results instantly.
Using react-live
import { LiveProvider, LiveEditor, LiveError, LivePreview } from 'react-live'
<LiveProvider code={`
function Greeting({ name }) {
return <h3>Hello, {name}! 👋</h3>
}
render(<Greeting name="World" />)
`}>
<LiveEditor />
<LiveError />
<LivePreview />
</LiveProvider>Custom Playground Component
Sandpack (by CodeSandbox)
If you want a full mini-IDE embedded in your page:
import { Sandpack } from '@codesandbox/sandpack-react'
<Sandpack
template="react"
files={{
'/App.js': `export default function App() {
return <h1>Hello MDX!</h1>
}`,
}}
options={{
showConsole: true,
showLineNumbers: true,
editorHeight: 300,
}}
/>When to Use Live Editors
- Component documentation — let people tweak props and see what happens
- Tutorials — learning by doing beats reading
- API exploration tools
- Design system playgrounds
Diff Syntax
Show what changed between two versions of code:
- const oldVariable = 'deprecated'
+ const newVariable = 'modern'
function processData(input) {
- return legacyTransform(input)
+ return modernTransform(input, { validate: true }) }
Styled Diff Block
You can combine diff with a title to show migration steps:
import React from 'react'
- import { render } from 'react-dom'
+ import { createRoot } from 'react-dom/client'
function App() { return <div>Hello</div> }
- render(<App />, document.getElementById('root'))
+ const root = createRoot(document.getElementById('root')) + root.render(<App />)
Diff Component
import { DiffBlock } from '../components/DiffBlock'
<DiffBlock
language="json"
title="package.json changes"
code={`
{
"dependencies": {
- "react": "^17.0.0",
- "react-dom": "^17.0.0"
+ "react": "^18.0.0",
+ "react-dom": "^18.0.0"
}
}
`}
/>Combining Multiple Features
You can stack these features together. Here's a code block with a file path, line numbers, highlighting, and syntax coloring all at once:
import { useState, useEffect } from 'react'
export function useDebounce<T>(value: T, delay: number): T { const [debouncedValue, setDebouncedValue] = useState<T>(value)
useEffect(() => { const timer = setTimeout(() => setDebouncedValue(value), delay) return () => clearTimeout(timer) }, [value, delay])
return debouncedValue }
Code Groups / Tabs
Show the same thing in multiple package managers or languages:
import { CodeGroup } from '../components/CodeGroup'
<CodeGroup>npm install @mdx-js/react
yarn add @mdx-js/react
pnpm add @mdx-js/react
</CodeGroup>Best Practices
- Always specify the language — Without it, there's no syntax highlighting.
- One concept per block — Don't dump 200 lines with no explanation.
- Use real names —
userName, notfoo.fetchOrders(), notdoThing(). - Highlight what matters — Guide the reader's eye to the important lines.
- Include file names — People need to know where code goes.
- Add copy buttons — Readers will copy-paste. Make it easy.
- Test your examples — Broken code kills your credibility instantly.
- Use diff for changes — Way clearer than writing "now change line 5 to..."
- Indent consistently — 2 spaces for JS/TS is standard.
Common Mistakes
Wrong Language Identifier
{/* ❌ BAD — "react" isn't a valid language */}function App() { return <div /> }
{/* ✅ GOOD — Use "jsx" or "tsx" */}
function App() { return <div /> }
Content on the Same Line as the Fence
{/* ❌ BAD — Content on same line as opening fence */}const y = 2
{/* ✅ GOOD — Content starts on next line */}
const x = 1 const y = 2
Forgetting to Close the Fence
{/* ❌ BAD — Missing closing fence breaks the page */}function broken() { // oops, no closing fence
{/* ✅ GOOD — Always close your fences */}
function working() {
// properly closed
}
### Mixing Tabs and Spaces{/* ❌ BAD — Inconsistent indentation */}
def calculate():
result = 0 # 4 spaces
total = 0 # 1 tab (invisible difference, visible error){/* ✅ GOOD — Consistent indentation */}
def calculate():
result = 0
total = 0
### Dumping Too Much Code at Once{/* ❌ BAD — 100+ lines with no explanation */}
// Entire file dumped with no guidance...{/* ✅ GOOD — Break into focused chunks with explanation */}
interface User { name: string; email: string }Then implement the interface:
class UserService implements UserRepository { ... }
## Quick Reference
| Feature | Syntax/Method |
|:--------|:-------------|
| Inline code | `` `code` `` |
| Fenced block | ```` ``` ```` |
| Language | ```` ```language ```` |
| Line highlight | ```` ```js {1,3-5} ```` |
| Line numbers | ```` ```js showLineNumbers ```` |
| File name | ```` ```js title="file.js" ```` |
| Diff | ```` ```diff ```` with `+` / `-` |
| Copy button | Component override |
| Live editor | react-live or Sandpack |
Code blocks are the backbone of any technical site. Set up a good code block system early — with copy buttons, syntax highlighting, and file names — and your readers will have a much better time.Exam Focus
Revise definitions, diagrams, examples, and short-answer points for Code Blocks in MDX.
Interview Use
Prepare one clear explanation, one practical example, and one common mistake for this MDX Tutorial topic.
Search Terms
mdx-tutorial, mdx tutorial, mdx, tutorial, basics, code, blocks, code blocks in mdx
Related MDX Tutorial Topics