If you've exported technical docs from Markdown to PDF, you've probably seen it: code in a proportional font, no highlighting, lines running off the page, indentation gone. The prose comes through fine and the code, the part your readers actually need, doesn't.
Here's why that happens and how to get code blocks into a PDF intact.
How do you convert Markdown to PDF with syntax highlighting for code blocks?
Give every fenced code block a language tag (python `, `js), paste the Markdown into a converter that highlights code in the PDF itself, and download. In MDTool's Markdown to PDF converter that's three steps:
- Paste your Markdown. Check that each code fence names its language.
- Pick a theme: GitHub, Academic, Minimal or Dark. Each one has its own highlight palette, so keywords, strings, numbers and comments are colored differently in the PDF.
- Click Download PDF. Code keeps its indentation and colors, Mermaid blocks (
`mermaid) become real diagrams, and the whole conversion runs in your browser, so the code is never uploaded.
| What you need | How MDTool handles it |
|---|---|
| Colored syntax | highlight.js tokens with a palette per theme |
| Languages | JavaScript, TypeScript, Python, Bash, CSS, HTML/XML, JSON, YAML, SQL, Rust, Go, Dart |
| Monospace font | Courier for code in standard Latin characters |
| Indentation | Leading spaces preserved |
| Long lines | Wrapped to the page instead of cut off |
| Diagrams |
mermaid ` blocks rendered as diagrams |
| Selectable text | Yes: the PDF contains real text, not a screenshot |
| Page breaks |
<!-- pagebreak -->, \pagebreak, \newpage or <div class="page-break"></div> on its own line |
If you need this in a script or CI job instead, MDTool has no CLI; see Markdown to PDF with Python, Pandoc and Node for command-line options with highlighting.
Why Code Blocks Are the Hardest Part of Markdown to PDF Conversion
If you've ever tried to convert a Markdown file with code blocks into a PDF, you've almost certainly run into at least one of these problems:
- Monospace font not used, so code renders in the same proportional font as body text
- No syntax highlighting, so everything is plain black text
- Lines overflow the page and get cut off
- Indentation is lost, making code unreadable
- Background color disappears, so code blocks look identical to normal paragraphs
These aren't random bugs. They happen because most tools take a shortcut: they parse Markdown into HTML, then dump it into a PDF renderer that ignores CSS entirely. Code blocks depend heavily on CSS (font-family: monospace, white-space: pre, overflow-x: auto, background colors), and when CSS is stripped out, code becomes unreadable.
What Broken Code Blocks Look Like
Here's a concrete example. Take this simple JavaScript function in a Markdown file:
```javascript
async function fetchUser(id) {
const response = await fetch(`/api/users/${id}`);
if (!response.ok) throw new Error("User not found");
return response.json();
}
```
A broken PDF converter will output something like this in the PDF:
async function fetchUser(id) { const response = await fetch("/api/users/" + id); if (!response.ok) throw new Error("User not found"); return response.json(); }
No line breaks. No monospace. No background. Just a wall of text that's impossible to read.
A correct PDF converter preserves:
- The monospace font (
Courier,Fira Code, or similar) - Each line on its own line with proper indentation
- A light gray or dark background that visually separates code from prose
- Syntax-colored tokens (keywords, strings, functions) if it supports highlighting
The Real Code Example
Here's the full JavaScript snippet we'll use to test PDF output throughout this guide:
// Illustrative example: a minimal browser-side Markdown to PDF function.
// (Not MDTool's own code; MDTool builds a vector PDF with pdfmake instead.)
async function convertMarkdownToPDF(markdownContent) {
// Parse Markdown into an HTML string
const { marked } = await import("marked");
const htmlContent = marked.parse(markdownContent);
// Wrap in a styled document for proper PDF rendering
const styledHTML = `
<!DOCTYPE html>
<html>
<head>
<style>
body { font-family: Georgia, serif; line-height: 1.6; }
pre { background: #f4f4f4; padding: 1rem; border-radius: 4px; overflow-x: auto; }
code { font-family: 'Courier New', monospace; font-size: 0.9em; }
</style>
</head>
<body>${htmlContent}</body>
</html>
`;
// Use html2pdf.js (browser only - never run server-side)
const html2pdf = (await import("html2pdf.js")).default;
return html2pdf()
.set({ margin: 10, filename: "output.pdf", html2canvas: { scale: 2 } })
.from(styledHTML)
.save();
}
Notice the three things that make this work: the <pre> element gets a background and padding, code gets a monospace font, and html2pdf.js is imported dynamically so it only ever runs in the browser, never on the server where window is undefined.
The catch with this approach is that html2pdf.js takes a picture of the page with html2canvas. The code in the PDF is an image: you can't select or search it, and it blurs when you zoom. That's why MDTool switched to pdfmake, which writes the code into the PDF as real text, with the highlight colors applied to each token.
Step-by-Step: Convert Markdown with Code Blocks to PDF
Step 1: Paste Your Markdown
Open a converter that highlights code in the PDF itself, such as MDTool's Markdown to PDF, and paste your Markdown. Your code blocks should be fenced with triple backticks and include a language identifier:
```javascript
// your code here
```
Language identifiers like javascript, python, bash, typescript, and json enable syntax highlighting. Without one, the converter tries to guess the language, which works for common code but is less reliable, so tag your fences.
Step 2: Check the Live Preview
The preview panel renders your Markdown in real time. Before downloading, verify that:
- Code blocks have a distinct background
- Indentation is preserved (4-space indents should look like 4 spaces, not 1)
- Long lines wrap correctly or have a horizontal scrollbar hint
If the preview looks right, the PDF will match, because the converter uses the same rendered HTML for both.
Step 3: Choose a Theme (Optional)
Light themes (GitHub, Academic, Minimal) work best for printed PDFs because they use dark text on a white or light-gray code background, exactly what you'd expect on paper. Dark themes look great on screen but can waste toner and reduce readability when physically printed.
Step 4: Download the PDF
Click Download PDF. The conversion happens entirely in your browser using pdfmake, so your Markdown never leaves your machine, and no server processes it.
Tip: For technical documentation, use the GitHub theme. It matches the code block style developers already recognize from GitHub READMEs.
Common Problems and Fixes
Problem: Code lines are cut off at the right edge of the page
Fix: Make sure your code lines are under ~80 to 90 characters. The converter wraps very long lines, but it can't reformat code logic. Break long lines manually before converting.
Problem: Syntax highlighting doesn't appear
Fix: Add a language tag after the opening triple backtick. js ` instead of just `.
Problem: Indentation collapses to a single space
Fix: Use spaces, not tabs. Some PDF renderers collapse tab characters. Replace tabs with 2 or 4 spaces.
Problem: Code block background is hard to see when printed
Fix: Every theme gives code blocks a tinted background and a border, but the light themes use a very pale gray that some printers barely show. Academic has the darkest code border of the light themes. For on-screen reading, the Dark theme gives the strongest contrast.
Problem: A Mermaid diagram shows up as code
Fix: The fence must be tagged exactly `mermaid. If the diagram has a syntax error, the block stays as code; check the live preview, which shows the same result the PDF will.
How MDTool Handles Code Blocks Differently
Most online Markdown-to-PDF converters run on the server: they accept your Markdown, process it with a headless browser or a PDF library, and return a file. The problem is CSS support in these pipelines is inconsistent, especially for code blocks.
MDTool takes a different approach. The entire conversion runs client-side in your browser using pdfmake. This means:
- Real text, not a screenshot: code in the PDF is selectable, searchable and sharp at any zoom
- No data sent to a server: your code stays on your machine
- Consistent output: the preview and the PDF come from the same rendered HTML, with highlight colors carried into the PDF and Mermaid diagrams drawn before the PDF is built
This is especially important for code blocks, which depend on whitespace, a monospace font and per-token colors that many converters discard.
For GitHub READMEs specifically, check out our guide on converting GitHub READMEs to PDF. It covers badge handling and image-heavy READMEs alongside code blocks. If you need a refresher on fenced code block syntax itself, see the Markdown cheatsheet.
Frequently Asked Questions
Q: Does MDTool support syntax highlighting for all programming languages?
It supports JavaScript, TypeScript, Python, Bash, JSON, YAML, HTML/XML, CSS, SQL, Go, Rust and Dart. The underlying parser uses marked with highlight.js for token coloring. If your language isn't highlighted, formatting (monospace font, background, indentation) is still preserved, and only color tokens are skipped.
Q: Will the code block look the same in the PDF as in the preview?
Very close. The PDF is built from the same rendered HTML you see in the preview, with the same token colors, indentation and diagrams. Fonts can differ slightly: code uses Courier in the PDF, and long lines wrap to fit the page.
Q: Can I convert a file with hundreds of lines of code?
Yes. Because the PDF is built as text rather than as an image, long files stay small and sharp. Very large documents take longer to build in the browser, so for files with thousands of lines of code, split them into sections or use page breaks to keep each section readable.
Q: My code has backticks inside the code block. How do I escape them?
Use four backticks to open and close the fenced block instead of three. This allows triple backticks inside the block without escaping.
Q: Is there a file size limit?
There's no server-side limit because conversion happens in your browser. The practical limit is your device's RAM, and most modern machines handle Markdown files up to several megabytes without issue.
Q: Can I use MDTool for confidential code?
Yes. Because conversion is entirely client-side, your code is never transmitted to any server. You can use it for proprietary or sensitive code with confidence.
I build MDTool, the free browser converter used in this post. It's open source (MIT), so if something breaks in it, open an issue or tell me in the comments.
Top comments (0)