Your AI just generated a perfectly formatted response, but it’s trapped in the wrong format. The ChatGPT output arrives as Markdown with headers, lists, and code blocks. You need it as HTML for an email. Or the opposite: you scraped a webpage and need clean Markdown for your RAG pipeline. The format mismatch breaks everything downstream.
The Markdown node solves this with two-way conversion between Markdown and HTML. No coding required. It handles GitHub Flavored Markdown, preserves code blocks, converts tables, and gives you control over exactly how the output renders. Whether you’re formatting AI responses for customers or preparing documents for language models, this node bridges the format gap.
The Format Conversion Challenge
Format mismatches create silent failures:
- AI models output Markdown, but email clients need HTML
- Web scraping returns HTML, but LLMs process Markdown better
- Report generators expect HTML, but your data is in Markdown
- CMS platforms have specific format requirements that don’t match your source
Manual conversion is tedious and error-prone. Copy-pasting through online converters breaks workflows. The Markdown node automates the transformation so your data flows seamlessly between systems.
What You’ll Learn
- When to use the Markdown node versus alternatives
- Both conversion modes: Markdown to HTML and HTML to Markdown
- All configuration options for each mode
- GitHub Flavored Markdown features and when to enable them
- Processing AI/ChatGPT responses for emails and documents
- Preparing content for RAG pipelines and vector databases
- Creating HTML reports from structured data
- Troubleshooting common conversion problems
- Best practices for reliable format transformations
When to Use the Markdown Node
Before adding the node, confirm it’s the right tool for your task. The Markdown node handles format conversion, not content extraction or generation.
| Scenario | Use Markdown Node? | Alternative |
|---|---|---|
| Convert AI response to HTML for email | Yes | Direct solution |
| Convert scraped HTML to Markdown for LLM | Yes | Direct solution |
| Generate HTML report from Markdown data | Yes | Direct solution |
| Extract specific data from HTML | No | HTML node |
| Create HTML from scratch with expressions | No | HTML node (Generate HTML Template) |
| Complex text manipulation with logic | No | Code node |
| Simple string formatting | No | Expressions with Edit Fields |
| PDF to text extraction | No | Extract from File node |
Rule of thumb: Use the Markdown node when you have complete content in one format that needs to become another format. For partial extraction, generation from data, or complex transformations, use other nodes.
When Not to Use the Markdown Node
The Markdown node converts entire content blocks. It doesn’t:
| Limitation | What Happens | Better Approach |
|---|---|---|
| Extract specific elements from HTML | Converts everything, not selections | Use HTML node with CSS selectors |
| Handle mixed format content | Treats input as one format | Split content first, then convert |
| Process binary files | Expects string input | Extract text first with Extract from File |
| Generate HTML with dynamic data | Only converts existing content | Use HTML node’s template operation |
| Clean malformed HTML | May produce unexpected output | Clean HTML first with Code node |
Understanding the Two Conversion Modes
The Markdown node operates in two distinct modes. Each uses a different parsing library optimized for its direction.
Markdown to HTML
Converts Markdown syntax into valid HTML. Uses the Showdown library, one of the most widely adopted Markdown parsers.
Input example:
# Welcome
This is **bold** and this is *italic*.
- Item one
- Item two
`inline code` and:
```javascript
const x = 1;
```
Output:
<h1>Welcome</h1>
<p>This is <strong>bold</strong> and this is <em>italic</em>.</p>
<ul>
<li>Item one</li>
<li>Item two</li>
</ul>
<p><code>inline code</code> and:</p>
<pre><code class="javascript language-javascript">const x = 1;
</code></pre>
Best for:
- Formatting AI responses for email
- Converting documentation for web display
- Generating HTML snippets for CMS platforms
- Creating formatted content for HTML-based systems
HTML to Markdown
Converts HTML content into clean Markdown syntax. Uses the node-html-markdown library, designed specifically for this conversion direction.
Input example:
<h1>Product Details</h1>
<p>This item is <strong>currently on sale</strong>.</p>
<ul>
<li>Feature A</li>
<li>Feature B</li>
</ul>
<a href="https://example.com">Learn more</a>
Output:
# Product Details
This item is **currently on sale**.
- Feature A
- Feature B
[Learn more](https://example.com)
Best for:
- Preparing scraped content for LLM processing
- Converting HTML emails to readable format
- Creating Markdown documentation from web content
- RAG pipeline document preparation
Quick Comparison
| Aspect | Markdown to HTML | HTML to Markdown |
|---|---|---|
| Parser library | Showdown | node-html-markdown |
| Primary use | Display/email formatting | LLM/document preparation |
| Input format | Markdown text | HTML markup |
| Output format | HTML markup | Markdown text |
| GFM support | Configurable options | Automatic handling |
| Code block handling | Preserves with language class | Converts to fenced blocks |
Your First Markdown Conversion
Let’s build a simple workflow that converts AI-generated Markdown to HTML for email.
Step 1: Add the Markdown Node
- Open your n8n workflow
- Click + to add a node
- Search for “Markdown”
- Select the Markdown node
Step 2: Configure the Mode
- Set Mode to “Markdown to HTML”
- The input field label changes to “Markdown”
Step 3: Provide Input Content
For testing, enter sample Markdown directly:
# Weekly Report
Here are this week's highlights:
- Completed **3 major features**
- Fixed *12 bugs*
- Deployed to production
## Next Steps
1. Review feedback
2. Plan sprint
Or reference data from a previous node:
{{ $json.aiResponse }}
Step 4: Test and Verify
Click Test step. The output appears in the data field (or your configured destination):
<h1>Weekly Report</h1>
<p>Here are this week's highlights:</p>
<ul>
<li>Completed <strong>3 major features</strong></li>
<li>Fixed <em>12 bugs</em></li>
<li>Deployed to production</li>
</ul>
<h2>Next Steps</h2>
<ol>
<li>Review feedback</li>
<li>Plan sprint</li>
</ol>
Step 5: Use the Output
Access the converted content in subsequent nodes:
{{ $json.data }}
Connect to an email node, HTTP Request, or any node that accepts HTML content.
Node Parameters Deep Dive
Understanding every parameter prevents conversion surprises.
Mode
| Mode | Description |
|---|---|
| Markdown to HTML | Converts Markdown syntax to HTML markup |
| HTML to Markdown | Converts HTML markup to Markdown syntax |
The mode you select determines which options appear below.
Input Field
For Markdown to HTML: Enter Markdown content directly or use an expression to reference data from previous nodes.
For HTML to Markdown: Enter HTML content directly or reference it with an expression.
Expression examples:
// Reference a specific field
{{ $json.content }}
// Reference nested data
{{ $json.response.message }}
// Reference from a named node
{{ $('AI Agent').item.json.output }}
Destination Key
By default, the converted output goes to a field called data. You can customize this:
| Setting | Output Location |
|---|---|
data (default) | $json.data |
htmlContent | $json.htmlContent |
body.content | $json.body.content (nested) |
results.formatted | $json.results.formatted (nested) |
Nested paths are powerful for organizing output alongside other data. The node creates the nested structure automatically.
Markdown to HTML Options
These options appear when Mode is set to “Markdown to HTML”. They control how specific Markdown features convert to HTML.
Add Blank to Links
When enabled, adds target="_blank" to all links so they open in new tabs.
| Setting | Output |
|---|---|
| Disabled | <a href="...">Link</a> |
| Enabled | <a href="..." target="_blank">Link</a> |
Use when: Creating HTML for web pages or emails where you want external links to open separately.
Automatic Linking
Automatically converts URLs and email addresses to clickable links, even without Markdown link syntax.
Input:
Visit https://example.com or email [email protected]
Output (enabled):
<p>Visit <a href="https://example.com">https://example.com</a> or email <a href="mailto:[email protected]">[email protected]</a></p>
Use when: Processing content where URLs aren’t already formatted as links.
Emoji Support
Converts emoji shortcodes to actual emoji characters.
Input:
Great job! :thumbsup: :rocket:
Output (enabled):
<p>Great job! 👍 🚀</p>
Use when: Processing content from platforms that use GitHub-style emoji shortcodes.
Tables
Enables GitHub Flavored Markdown table syntax support.
Input:
| Name | Price |
| ---- | ----- |
| Item | $10 |
Output (enabled):
<table>
<thead>
<tr>
<th>Name</th>
<th>Price</th>
</tr>
</thead>
<tbody>
<tr>
<td>Item</td>
<td>$10</td>
</tr>
</tbody>
</table>
Use when: Your Markdown contains tabular data. Most AI models output tables in this format.
GitHub Flavored Markdown (GFM)
Enables the full GFM specification, which includes:
- Tables
- Task lists (
- [x] Done) - Strikethrough (
~~text~~) - Autolinks
- Disallowed raw HTML
Recommendation: Enable this for most use cases, especially when processing AI output. ChatGPT, Claude, and other models typically output GFM-compatible Markdown.
Task Lists
Converts checkbox syntax to HTML checkbox elements.
Input:
- [x] Completed task
- [ ] Pending task
Output (enabled):
<ul>
<li><input type="checkbox" checked disabled> Completed task</li>
<li><input type="checkbox" disabled> Pending task</li>
</ul>
Use when: Converting task lists or TODO items from AI responses or project management tools.
Strikethrough
Enables ~~strikethrough~~ syntax support.
Input:
The price is ~~$50~~ $30
Output (enabled):
<p>The price is <del>$50</del> $30</p>
Split Level
Controls how headers create document structure. Advanced option for specific formatting needs.
HTML to Markdown Options
These options appear when Mode is set to “HTML to Markdown”. They control the style of the generated Markdown.
Bullet List Marker
Choose the character for unordered lists.
| Option | Output |
|---|---|
- (default) | - Item |
* | * Item |
+ | + Item |
Recommendation: Use - for compatibility with most Markdown parsers and AI models.
Code Block Style
Choose how code blocks render in the output.
| Option | Output |
|---|---|
| Fenced (default) | ```code``` |
| Indented | Four-space indentation |
Recommendation: Use Fenced for better readability and language specification support.
Emphasis Delimiter
Choose the character for italic text.
| Option | Output |
|---|---|
_ (default) | _italic_ |
* | *italic* |
Heading Style
Choose how headers render in Markdown.
| Option | Output |
|---|---|
| ATX (default) | # Heading |
| Setext | Heading with underline (=== or ---) |
Recommendation: Use ATX for cleaner, more consistent output. Setext only supports H1 and H2.
Link Style
Choose how links render.
| Option | Output |
|---|---|
| Inlined (default) | [text](url) |
| Referenced | [text][1] with [1]: url at bottom |
Recommendation: Use Inlined for simpler documents. Referenced is useful for documents with many repeated links.
Strong Delimiter
Choose the character for bold text.
| Option | Output |
|---|---|
** (default) | **bold** |
__ | __bold__ |
Real-World Examples
Example 1: Formatting AI Responses for Email
Scenario: ChatGPT generates a customer response in Markdown. You need HTML for an email.
Workflow:
Webhook → OpenAI Chat → Markdown (MD→HTML) → Send Email
Markdown Node Configuration:
- Mode: Markdown to HTML
- Markdown:
{{ $json.message.content }} - Options:
- Tables: Enabled
- GFM: Enabled
- Add Blank to Links: Enabled
Why this works: AI models output Markdown by default. The Markdown node converts it to email-ready HTML while preserving all formatting.
Example 2: Preparing Scraped Content for LLM Processing
Scenario: You scrape a webpage and need to feed it to an AI for summarization. HTML is noisy. Markdown is cleaner.
Workflow:
HTTP Request → HTML Extract → Markdown (HTML→MD) → AI Agent
Markdown Node Configuration:
- Mode: HTML to Markdown
- HTML:
{{ $json.data }} - Destination Key:
cleanContent - Options:
- Code Block Style: Fenced
- Heading Style: ATX
Why this works: Converting HTML to Markdown removes noise (tags, attributes) while preserving structure (headings, lists). This improves LLM comprehension and reduces token usage.
For more on optimizing AI workflows, see our guide to AI agents vs LLM chains.
Example 3: Generating HTML Reports from Data
Scenario: You have structured data and need to generate an HTML report.
Workflow:
Database → Code (generate MD) → Markdown (MD→HTML) → Convert to File → Email
Code Node (JavaScript):
const items = $input.all();
const data = items[0].json;
let markdown = `# Sales Report
## Summary
- Total Sales: **$${data.totalSales.toLocaleString()}**
- Orders: **${data.orderCount}**
- Average Order: **$${(data.totalSales / data.orderCount).toFixed(2)}**
## Top Products
| Product | Units | Revenue |
| ------- | ----- | ------- |
`;
data.topProducts.forEach(p => {
markdown += `| ${p.name} | ${p.units} | $${p.revenue} |\n`;
});
return [{ json: { markdown } }];
Markdown Node Configuration:
- Mode: Markdown to HTML
- Markdown:
{{ $json.markdown }} - Options:
- Tables: Enabled
- GFM: Enabled
The HTML output can then be converted to a file or embedded in an email.
Example 4: RAG Pipeline Document Preparation
Scenario: You’re building a RAG system. Documents arrive as HTML but need to be chunked effectively for vector storage.
Workflow:
HTTP Request → Markdown (HTML→MD) → Split (by headers) → Embeddings → Vector Store
Why Markdown for RAG:
Markdown preserves document structure (headers, lists, code blocks) while eliminating HTML noise. This structure helps the text splitter create meaningful chunks at logical boundaries.
Markdown Node Configuration:
- Mode: HTML to Markdown
- HTML:
{{ $json.data }} - Options:
- Heading Style: ATX (consistent
#format for splitting) - Code Block Style: Fenced
- Heading Style: ATX (consistent
Splitting strategy:
Use a text splitter that recognizes Markdown headers. Split at ## (H2) level to create topic-focused chunks that maintain context.
For comprehensive data transformation strategies, see our data transformation guide.
Example 5: Newsletter Content Formatting
Scenario: You aggregate content from multiple sources and need to format a newsletter.
Workflow:
RSS Feeds → Aggregate → Code (compile MD) → Markdown (MD→HTML) → Email
Code Node (JavaScript):
const items = $input.all();
let newsletter = `# This Week's Highlights
`;
items.forEach((item, i) => {
const data = item.json;
newsletter += `## ${i + 1}. ${data.title}
${data.summary}
[Read more](${data.link})
---
`;
});
newsletter += `
*Unsubscribe: [Click here]({{unsubscribeLink}})*
`;
return [{ json: { newsletter } }];
Markdown Node Configuration:
- Mode: Markdown to HTML
- Options:
- Add Blank to Links: Enabled (opens in new tab)
- GFM: Enabled
Working with AI Responses
AI models are the most common source of Markdown content in n8n workflows. Here’s how to handle them reliably.
Common AI Output Patterns
Most AI models (ChatGPT, Claude, Gemini) output responses in Markdown format:
Here's a summary of the document:
## Key Points
1. **First point** with details
2. **Second point** with more info
### Code Example
```python
def example():
return "Hello"
```
Let me know if you need clarification.
Recommended Options for AI Content
For typical AI response processing:
| Option | Setting | Why |
|---|---|---|
| Tables | Enabled | AI often outputs tabular data |
| GFM | Enabled | AI follows GFM conventions |
| Task Lists | Enabled | AI generates checklists |
| Strikethrough | Enabled | AI uses this for corrections |
| Add Blank to Links | Enabled | Safer for email links |
Preserving Code Blocks
AI responses often contain code. The Markdown node preserves code blocks with language specification:
Input:
Here's how to do it:
```javascript
const result = await fetch(url);
```
Output:
<p>Here's how to do it:</p>
<pre><code class="javascript language-javascript">const result = await fetch(url);
</code></pre>
The language-javascript class enables syntax highlighting if your display system supports it.
Handling Edge Cases
Problem: AI sometimes includes extra explanation before/after the actual content.
Solution: Use an Edit Fields node to extract just the relevant portion before conversion:
// Extract content between markers
{{ $json.response.match(/```markdown([\s\S]*?)```/)?.[1] || $json.response }}
Problem: AI outputs inconsistent heading levels.
Solution: Normalize in a Code node before conversion, or accept the inconsistency and style with CSS after conversion.
Troubleshooting Common Issues
Output Not Rendering Correctly
Symptom: HTML appears as plain text instead of formatted content.
Cause: The receiving system isn’t interpreting the content as HTML.
Solutions:
- Check if the email node’s content type is set to HTML
- Verify the destination system accepts HTML
- Wrap output in proper HTML document structure if needed:
{{ '<html><body>' + $json.data + '</body></html>' }}
Missing Tables in Output
Symptom: Table Markdown appears as plain text lines.
Cause: Tables option not enabled.
Solution: Enable the Tables option in Markdown to HTML mode.
Special Characters Breaking Conversion
Symptom: Characters like <, >, & cause issues.
Cause: These characters have special meaning in HTML.
For MD→HTML: The converter escapes these automatically. If you’re seeing issues, the input may already be partially HTML.
For HTML→MD: Ensure your input is valid HTML. Run it through a validator if needed.
Nested Content Problems
Symptom: Deeply nested lists or quotes don’t convert properly.
Cause: Parser limitations with complex nesting.
Solution: Simplify the structure before conversion, or use the Code node for complex transformations.
Inconsistent Line Breaks
Symptom: Line breaks appear differently than expected.
Cause: Markdown has specific rules for line breaks (two spaces at end of line, or blank line for paragraph).
Solution: Ensure your Markdown source follows standard line break conventions. Or post-process the HTML to add <br> where needed.
For workflow debugging help, try our workflow debugger tool.
Pro Tips and Best Practices
1. Test with Representative Content
Before deploying, test with actual content that matches your production data. Edge cases in real content often differ from simple test examples.
2. Chain Transformations Strategically
Complex transformations often work better as multiple steps:
Raw Data → Code (prepare) → Markdown (convert) → Edit Fields (clean)
Rather than trying to do everything in one node.
3. Consider Performance
The Markdown node processes content synchronously. For large documents or high-volume workflows:
- Process items individually rather than concatenating into huge strings
- Consider splitting very large documents before conversion
- Monitor execution time with batch processing strategies
4. Validate Expressions
When using expressions to reference input content, ensure the field exists:
{{ $json.content || '' }}
This prevents errors when the field is undefined.
For expression validation, use our expression validator tool.
5. Use Destination Key for Clean Output
Rather than letting converted content overwrite your input, use a specific destination key:
Destination Key: convertedHtml
This preserves the original content alongside the converted version.
6. Document Your Options
When using specific options, add a sticky note explaining why:
Tables: ON - AI reports include pricing tables
GFM: ON - Standard for ChatGPT output
Future you (or your team) will appreciate the context.
7. Handle Empty Content
Add a check before conversion to avoid processing empty strings:
If Node → Check: {{ $json.content.length > 0 }}
→ True: Markdown Node
→ False: Skip/Default
When to Get Help
Some scenarios benefit from expert assistance:
- Complex multi-format workflows with conditional conversion logic
- High-volume processing requiring optimization
- Integration with specific CMS platforms that have unique requirements
- Custom Markdown extensions beyond standard GFM
Our workflow development services can build production-ready solutions. For architectural guidance, explore our n8n consulting services.
Frequently Asked Questions
What’s the difference between the Markdown node and using expressions for text formatting?
Expressions handle simple string operations. The Markdown node performs full document parsing and conversion.
Use expressions for:
- String concatenation:
{{ $json.first + ' ' + $json.last }} - Case changes:
{{ $json.text.toUpperCase() }} - Number formatting:
{{ $json.price.toFixed(2) }}
Use the Markdown node for:
- Converting headers, lists, code blocks, and tables
- Full Markdown-to-HTML or HTML-to-Markdown conversion
- Preserving document structure during format changes
The key difference: expressions manipulate strings character by character. The Markdown node understands document structure and converts it properly.
Why isn’t my Markdown rendering correctly in Notion or Google Docs?
Notion and Google Docs don’t accept raw HTML. They have their own internal formats.
When you send converted HTML to these platforms, they treat it as plain text because they expect their native format, not HTML strings.
For Notion:
- Use the Notion node’s native formatting options
- Or use Notion’s Markdown import feature directly
For Google Docs:
- Use the Google Docs API with proper document structure
- HTML strings won’t render as formatted content
The Markdown node works well with:
- Email clients (Gmail, Outlook, etc.)
- Web pages and HTML-based CMS platforms
- Any system that interprets HTML markup
How do I preserve code blocks when converting AI responses?
The Markdown node preserves code blocks automatically. Fenced code blocks convert to <pre><code> HTML tags with language classes intact.
Requirements for proper conversion:
- Input must use fenced code blocks (triple backticks)
- Language specification is preserved as a CSS class
If code isn’t converting properly:
- Check that the AI actually used fenced blocks (not just indentation)
- Prompt your AI: “Always use fenced code blocks with language specification”
- For HTML-to-Markdown, select “Fenced” code block style option
Example output:
<pre><code class="javascript language-javascript">const x = 1;</code></pre>
The language-javascript class enables syntax highlighting in systems that support it.
Can I convert partial HTML within a larger text block?
The Markdown node treats the entire input as one format. It cannot detect and convert only HTML portions within mixed content.
If you have mixed content, you have two options:
Option 1: Split and recombine
- Extract just the HTML portion using expressions or a Code node
- Convert it with the Markdown node
- Recombine with the other content
Option 2: Use a Code node
Write custom logic that handles mixed content using a parsing library.
Best practice: Restructure your workflow to keep formats separate. This is cleaner and more maintainable than trying to handle mixed content in a single field.
Which GFM options should I enable for typical use cases?
Different use cases need different options enabled.
For AI/ChatGPT output processing:
- Tables: Enabled
- GFM (main toggle): Enabled
- Task Lists: Enabled
- Strikethrough: Enabled
For basic documentation conversion:
- Tables: Enabled
- GFM: Enabled
For email content:
- All above options
- Add Blank to Links: Enabled (opens links in new tabs)
Emoji option:
- Enable only if content uses shortcodes (
:thumbsup:format) - Leave disabled if content uses Unicode emoji (👍) directly