A live Markdown previewer with a split-pane editor — type on the left, see rendered HTML instantly on the right. Built with a custom Markdown parser in pure JavaScript.
Convert the most common Markdown syntax to HTML using regex replacements.
function parseMarkdown(md) {
let html = md
// Escape HTML (security: prevent XSS from user input)
.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>')
// Headers (process from h3 down to h1 to avoid conflicts)
.replace(/^### (.+)$/gm, '<h3>$1</h3>')
.replace(/^## (.+)$/gm, '<h2>$1</h2>')
.replace(/^# (.+)$/gm, '<h1>$1</h1>')
// Code blocks (triple backticks)
.replace(/```([\s\S]*?)```/gm, '<pre><code>$1</code></pre>')
// Inline code
.replace(/`([^`]+)`/g, '<code>$1</code>')
// Bold + italic
.replace(/\*\*\*(.+?)\*\*\*/g, '<strong><em>$1</em></strong>')
.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>')
.replace(/\*(.+?)\*/g, '<em>$1</em>')
// Links and images
.replace(/!\[([^\]]*)\]\(([^)]+)\)/g, '<img src="$2" alt="$1" style="max-width:100%">')
.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2" target="_blank">$1</a>')
// Blockquotes
.replace(/^> (.+)$/gm, '<blockquote>$1</blockquote>')
// Unordered lists (naive)
.replace(/^\- (.+)$/gm, '<li>$1</li>')
.replace(/(<li>.*<\/li>)/s, '<ul>$1</ul>')
// Paragraphs (double newlines)
.replace(/\n\n+/g, '</p><p>')
.replace(/\n/g, '<br>');
return '<p>' + html + '</p>';
}
// Test
console.log(parseMarkdown('# Hello\n\nThis is **bold** and *italic* text.'));
A textarea on the left updates the rendered HTML on the right with every keystroke.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Markdown Previewer</title>
<style>
* { margin:0; padding:0; box-sizing:border-box; }
body { font-family:'Segoe UI',sans-serif; background:#0f172a; color:#f8fafc; height:100vh; display:flex; flex-direction:column; }
header { padding:14px 20px; background:#1e293b; border-bottom:1px solid #334155; display:flex; justify-content:space-between; align-items:center; }
header h1 { font-size:1.1rem; }
.labels { display:flex; }
.label { width:50%; text-align:center; font-size:0.78rem; text-transform:uppercase; letter-spacing:0.06em; color:#64748b; padding:8px; background:#0f172a; border-bottom:1px solid #334155; }
.editor { display:flex; flex:1; overflow:hidden; }
#editor { width:50%; padding:20px; background:#0f172a; color:#f8fafc; font-family:'Cascadia Code','Courier New',monospace; font-size:0.9rem; line-height:1.7; border:none; resize:none; border-right:2px solid #334155; }
#editor:focus { outline:none; }
#preview { width:50%; padding:20px; overflow-y:auto; }
#preview h1,#preview h2,#preview h3 { margin:12px 0 8px; color:#f8fafc; }
#preview h1 { font-size:1.8rem; border-bottom:1px solid #334155; padding-bottom:8px; }
#preview h2 { font-size:1.4rem; }
#preview p { margin-bottom:12px; line-height:1.7; color:#cbd5e1; }
#preview code { background:#1e293b; padding:2px 6px; border-radius:4px; font-size:0.85em; color:#f59e0b; }
#preview pre { background:#1e293b; padding:16px; border-radius:10px; margin:12px 0; overflow-x:auto; }
#preview pre code { background:none; padding:0; color:#f8fafc; }
#preview a { color:#3b82f6; }
#preview blockquote { border-left:4px solid #3b82f6; margin:12px 0; padding:8px 16px; background:#162447; border-radius:0 8px 8px 0; }
#preview strong { color:#f8fafc; }
#preview ul { padding-left:20px; margin-bottom:12px; }
#preview li { line-height:1.8; color:#cbd5e1; }
</style>
</head>
<body>
<?php require_once __DIR__ . '/../promo_banner.php'; ?>
<header>
<h1>📝 Markdown Previewer</h1>
<span style="color:#64748b;font-size:0.8rem">Live preview as you type</span>
</header>
<div class="labels"><div class="label">Markdown</div><div class="label">Preview</div></div>
<div class="editor">
<textarea id="editor" spellcheck="false" placeholder="Type Markdown here..."></textarea>
<div id="preview"></div>
</div>
<script>
// Paste parseMarkdown() from Step 1 here, then:
const editor = document.getElementById('editor');
const preview = document.getElementById('preview');
const SAMPLE = `# Welcome to Markdown Previewer\n\nType in the **left panel** and see the rendered preview here.\n\n## Features\n\n- **Bold** text with \`**text**\`\n- *Italic* with \`*text*\`\n- \`Inline code\` with backticks\n- [Links](https://example.com)\n\n## Code blocks\n\n\`\`\`\nfunction hello() {\n console.log("Hello, World!");\n}\n\`\`\`\n\n> Blockquotes look like this.`;
editor.value = SAMPLE;
editor.addEventListener('input', () => {
preview.innerHTML = parseMarkdown(editor.value);
});
preview.innerHTML = parseMarkdown(SAMPLE);
</script>
</body>
</html>
You wrote a custom Markdown parser and a live-preview editor. The regex-replacement approach handles 95% of common Markdown in 20 lines. The split-pane UI pattern is also used in code editors (Monaco), diagramming tools (Mermaid), and note-taking apps.
Spotted a bug, broken code, or something that doesn't look right? Tell us what's off and we'll fix it.