The Problem That Drove Me Crazy
You know that feeling when you copy text from Medium or dev.to, and it pastes with a rainbow of background colors, broken formatting, and mysterious inline styles? Yeah, I got tired of that.
I needed a tool that could take messy web content and give me clean Markdown. Simple, right?
Turns out, every converter I tried was either broken, ugly, or missing critical features. Some stripped ALL formatting. Others kept the garbage backgrounds. None handled images in links properly.
So I built my own. And honestly? It turned out way better than I expected.
Official Website Link for Rich Text To Markdoen Converter
Code is OpenSource:
What I Built
100% Free Markdown Converter - a real-time, intelligent converter that transforms any web content into perfect Markdown. No server, no API calls, just pure client-side magic.
The Core Features I'm Most Proud Of
1. Smart Paste That Actually Works
This was the hardest part to nail. When you paste content, my converter:
- Strips all the garbage: Background colors, inline styles, CSS classes - gone
- Keeps the structure: Headings, bold, italic, links, images - preserved
- Handles edge cases: Images in links? Fixed. Anchor-linked headings? Cleaned. Code blocks with extra spacing? Trimmed perfectly.
I wrote a custom HTML cleaner that walks through every element and makes intelligent decisions about what to keep. It's not using some generic library - this is custom logic built from testing hundreds of real-world websites.
2. Real-Time Markdown Preview
Every keystroke updates the Markdown output instantly. No "Convert" button, no loading spinners. Type on the left, see Markdown on the right.
I implemented this with a custom HTML-to-Markdown parser that handles:
- All heading levels (H1-H6)
- Nested lists (both ordered and unordered)
- Tables with proper formatting
- Code blocks with intelligent whitespace handling
- Blockquotes that span multiple lines
3. The Image Intelligence I'm Really Proud Of
Here's a problem nobody else solved properly: websites wrap images in anchor tags for lightbox effects. Every converter I tested either:
- Kept the wrapper link (useless in Markdown)
- Broke the image entirely
- Generated malformed syntax
My solution: I detect if a link contains ONLY an image (no text), and extract just the image. If there's text too, it keeps the link structure. Simple logic, massive improvement.
// This took forever to get right
if (tag === "a") {
const imgEl = el.querySelector('img');
const hasText = el.textContent.trim().length > 0;
if (imgEl && !hasText) {
// Just output the image, skip the link wrapper
return `\n\n`;
}
}
4. Heading Link Stripping
Another annoying thing, dev.to, Medium, and other platforms wrap every heading in an anchor link for deep linking. When you convert, you get:
### [https://example.com#section-name](url)My Actual Heading
Completely broken.
My fix: I walk up the DOM tree when processing links. If a link is inside a heading element, I return only the text content. Clean headings, every time.
5. Code Block Perfection
Code blocks are deceptively hard. Paste code from GitHub, and it has extra newlines. From Stack Overflow? Different formatting. From your IDE? Yet another style.
I built smart trimming:
- Removes leading blank lines
- Removes trailing blank lines
- Preserves internal spacing (critical for Python, YAML, etc.)
Sounds simple, but getting this right took testing dozens of edge cases.
The UX Details That Make It Feel Premium
Professional Design Language
I didn't want another boring developer tool. I studied modern SaaS apps (Linear, Notion, Vercel) and built a dark theme with:
- Subtle gradients on the logo icon
- Smooth micro-animations on buttons and modals
- Proper elevation with layered shadows
- Consistent spacing using an 8px grid system
- Refined typography with -0.02em letter spacing on headings
Modal System Over Browser Prompts
Instead of ugly prompt()
dialogs for links/images/tables, I built a proper modal system:
- Backdrop blur effect
- Smooth slide-up animation with cubic-bezier easing
- Keyboard shortcuts (Enter to submit, Escape to close)
- Focus management (automatically focuses the right input)
- Form validation with helpful error messages
Resizable Panels
Want more editor space? Drag the divider. It changes to blue on hover, shows a col-resize cursor, and updates panel widths smoothly.
// Simple but effective
const percentage = ((e.clientX - containerRect.left) / containerRect.width) * 100;
if (percentage > 20 && percentage < 80) {
panels[0].style.flex = `0 0 ${percentage}%`;
panels[1].style.flex = `0 0 ${100 - percentage}%`;
}
Smart Empty States
Instead of blank panels screaming at you, I added friendly empty states:
- Large emoji icon (📝)
- Clear message about what to do
- Helpful tip about pasting
Live Statistics
Character, word, and line counts update in real-time. Useful for hitting article length targets or checking document size.
Toast Notifications
Every action gives feedback:
- Success toasts (green) for copy/download
- Error toasts (red) for validation failures
- Smooth slide-up animation from bottom-right
- Auto-dismiss after 3 seconds
Technical Decisions I Made
Why No Framework?
Vanilla JavaScript. No React, no Vue, no build step.
Why? Because:
- Performance: Zero framework overhead means instant load
- Simplicity: 500 lines of JS instead of 5000 with dependencies
- Maintainability: Anyone can read and understand the code
- File size: Single HTML file under 50KB
ContentEditable for the Win
I used contenteditable
for the editor instead of a textarea because:
- Native rich text support (bold, italic, etc.)
- Built-in undo/redo via
document.execCommand
- Paste handling with full HTML access
- Better UX for formatting tools
The trick was handling paste events properly to clean content before insertion.
Custom HTML-to-Markdown Parser
I could've used Turndown or similar libraries, but they don't handle edge cases well:
- Background colors in pasted content
- Anchor-wrapped headings
- Image-only links
- Excessive whitespace in code blocks
Building custom gave me full control over every conversion scenario.
Client-Side Only Architecture
Everything runs in your browser. No server, no API, no database.
Benefits:
- Privacy: Your content never leaves your device
- Speed: No network latency
- Reliability: Works offline (after initial load)
- Cost: Zero hosting/bandwidth costs
CSS Variables for Theming
I set up a complete design system with CSS variables:
:root {
--bg-primary: #0a0a0a;
--bg-secondary: #141414;
--accent-primary: #3b82f6;
--text-primary: #fafafa;
/* ...and 15 more */
}
This makes theming trivial. Want to add light mode? Just override the variables.
Features Breakdown
Text Formatting
-
Bold (⌘B) - Wraps in
**text**
-
Italic (⌘I) - Wraps in
*text*
-
Underline (⌘U) - Uses
<u>text</u>
-
Strikethrough - Converts to
~~text~~
Structure Elements
-
Headings (H1-H6) - Full support with proper
#
syntax - Paragraphs - Smart spacing
- Line breaks - Preserved correctly
-
Horizontal rules - Clean
---
output
Lists
- Bullet lists with proper indentation
- Numbered lists with sequential numbering
- Nested lists (though Markdown has limits here)
Rich Content
-
Links - Clean
[text](url)
format -
Images - Proper

syntax - Code blocks - Fenced with triple backticks
- Inline code - Wrapped in single backticks
-
Blockquotes - Multi-line support with
>
prefix - Tables - Full GFM-style tables with headers
Actions
- Copy (⌘S) - Clipboard copy with toast confirmation
- Download - Saves as .md file with timestamp
- Clear - Wipes everything (with confirmation)
- Undo (⌘Z) - Full history support
- Redo (⌘⇧Z) - Redo your undos
The Challenges I Overcame
1. Paste Event Hell
Handling paste is surprisingly complex. Different sources provide data differently:
- Some send
text/html
with full DOM - Others send
text/plain
only - Some have broken HTML with unclosed tags
- Many have inline styles that break everything
My solution: Always prefer HTML if available, clean it aggressively, then insert.
2. Selection Management for Modals
When you click "Insert Link", you need to remember where the cursor was. But opening a modal loses focus!
I implemented selection save/restore:
function saveSelection() {
const sel = window.getSelection();
if (sel.rangeCount > 0) {
savedSelection = sel.getRangeAt(0);
}
}
function restoreSelection() {
if (savedSelection) {
const sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(savedSelection);
}
}
Now modals work seamlessly without losing your place.
3. Code Block Whitespace
This one drove me nuts. Pasted code often has:
- 3-4 blank lines at the start
- 2-3 blank lines at the end
- But intentional blank lines in the middle (important!)
Can't just .trim()
because that removes internal spacing too.
My solution: Loop through lines, trim from start until non-empty, trim from end until non-empty, preserve everything else.
4. Table Cell Content with Newlines
Tables in Markdown can't have newlines in cells. But pasted tables often do!
I replace newlines with spaces during table processing:
const cells = Array.from(row.querySelectorAll("td, th"))
.map(c => processNode(c).trim().replace(/\n/g, ' '));
Simple fix, huge impact.
5. Responsive Design Without Breaking Features
Making this work on mobile was tricky:
- Toolbar buttons need to be accessible
- Panels need to stack vertically
- Modals need to fit small screens
- Touch scrolling needs to feel native
I used flexbox, careful media queries, and -webkit-overflow-scrolling: touch
to nail the mobile experience.
Use Cases I Designed For
1. Cross-Posting Blog Articles
Problem: You publish on Medium, want to post to dev.to
Solution: Copy from Medium, paste here, download Markdown, post to dev.to
2. GitHub README Creation
Problem: Writing Markdown syntax manually is tedious
Solution: Write in the editor with formatting buttons, copy the output
3. Cleaning Legacy HTML
Problem: Old content with inline styles and deprecated tags
Solution: Paste the mess, get clean modern Markdown
4. Documentation Writing
Problem: Need to create docs with code examples, tables, images
Solution: Use the formatting tools to build structured content
5. Quick Notes
Problem: Want to jot down formatted notes in Markdown
Solution: Type naturally, copy Markdown when done
Performance Optimizations
1. Debouncing Updates
Real-time conversion on every keystroke could lag. But it doesn't, because I update synchronously - the parsing is fast enough (<5ms for typical content).
2. Efficient DOM Manipulation
I use documentFragment
and batch operations where possible to minimize reflows.
3. CSS Hardware Acceleration
Animations use transform
and opacity
- GPU-accelerated properties that stay smooth at 60fps.
4. No Memory Leaks
Event listeners are properly managed. Selection objects are cleared when not needed.
What I Learned Building This
1. ContentEditable is Weird
contentEditable
has quirks. Different browsers handle formatting differently. Some create <b>
tags, others <strong>
. Had to normalize everything.
2. Markdown Has Limitations
Markdown can't represent everything HTML can. Merged table cells? Nope. Nested divs? Nope. Had to make smart compromises.
3. User Testing is Critical
I thought my first version was great. Then I tested on real websites and found:
- Background colors everywhere (missed in cleaning)
- Images wrapped in links (broke the output)
- Heading anchor links (created garbage)
Real content is messier than test content.
4. Details Matter
The difference between a good tool and a great tool is 100 small details:
- Tooltip positioning
- Button hover states
- Empty state messaging
- Error feedback
- Keyboard shortcuts
- Touch gestures
I sweated all of them.
5. Sometimes Vanilla is Best
Modern frameworks are amazing, but for a focused tool like this, vanilla JS was perfect. Faster development, better performance, easier maintenance.
The Numbers
Development Time: ~20 hours across 3 days
Lines of Code:
- JavaScript: ~500 lines
- CSS: ~800 lines
- HTML: ~200 lines
Total File Size: 48KB (uncompressed)
Dependencies: Zero
Browser Support: Chrome 90+, Firefox 88+, Safari 14+
Performance:
- Initial load: <100ms
- Paste to conversion: <50ms (for 5000 word articles)
- Memory usage: ~15MB (typical)
Features I'm Planning
Short Term
- [ ] Syntax highlighting for code blocks (using Prism.js)
- [ ] Dark/light theme toggle (already have CSS variables ready)
- [ ] Import from URL (fetch and convert any webpage)
- [ ] Markdown preview (rendered HTML view)
Medium Term
- [ ] Browser extension (convert pages with a click)
- [ ] Batch conversion (multiple files at once)
- [ ] Custom shortcuts (user-definable hotkeys)
- [ ] Export to PDF (via print styles)
Long Term
- [ ] Collaboration (share and edit together)
- [ ] Cloud sync (optional, via localStorage backup)
- [ ] Mobile apps (React Native wrapper)
- [ ] API (programmatic access)
Technical Stack Deep Dive
The Conversion Engine
At the heart of this is a recursive DOM walker:
function processNode(node) {
let result = "";
for (let child of node.childNodes) {
if (child.nodeType === Node.TEXT_NODE) {
result += child.textContent;
} else if (child.nodeType === Node.ELEMENT_NODE) {
result += processElement(child);
}
}
return result;
}
This walks every node in the pasted HTML, makes decisions about each element, and builds the Markdown string recursively.
The Cleaning System
Before conversion, I clean the HTML:
function cleanElement(element) {
const allowedTags = ['p', 'br', 'strong', 'b', 'em', 'i', 'u', 's', ...];
element.querySelectorAll('*').forEach(el => {
if (!allowedTags.includes(el.tagName.toLowerCase())) {
// Replace with children
const parent = el.parentNode;
while (el.firstChild) {
parent.insertBefore(el.firstChild, el);
}
parent.removeChild(el);
}
// Strip all style attributes
el.removeAttribute('style');
el.removeAttribute('class');
// ... keep only essential attributes
});
}
This ensures only semantic HTML makes it through.
The Modal System
I built a reusable modal system:
function showModal(modalId, onSubmit) {
saveSelection();
document.getElementById(modalId).classList.add('show');
// Focus management, keyboard handlers, etc.
}
Each modal (link, image, table) uses this base with custom submit handlers.
Design Philosophy
1. Zero Configuration
No settings menu, no preferences. It just works. Smart defaults for everything.
2. Instant Feedback
Every action shows immediate results. No loading states, no waiting.
3. Forgiving UX
Made a mistake? Undo it. Pasted wrong? Clear and try again. Modals have cancel buttons. Confirmations prevent data loss.
4. Keyboard-First
Power users can do everything without touching the mouse. Shortcuts for all common actions.
5. Beautiful by Default
Good design isn't decoration - it's clarity. Every pixel serves a purpose.
Why This Matters
Better tools make better work possible.
Before building this, I was:
- Manually cleaning HTML in VS Code
- Writing Markdown syntax by hand
- Copying formatting styles one element at a time
Now I paste and move on. This saves me 10-15 minutes per article. For someone who writes 3-4 articles a week, that's an hour saved weekly.
And it's not just time - it's mental energy. Not having to think about formatting means more energy for writing.
Open Source Considerations
I'm considering open sourcing this because:
Pros:
- Community contributions could add features faster
- Others could learn from the code
- Increased trust (source is visible)
Cons:
- Support burden (issues, PRs, questions)
- Feature creep (everyone wants their thing added)
- Competitors could clone it
Still deciding. Thoughts?
Try It Yourself
The best way to understand what I built is to use it:
- Open the app
- Go to any article on Medium, dev.to, or your blog
- Copy a few paragraphs with images and code
- Paste into the converter
- Watch the magic happen
You'll see instantly why I spent 20 hours on this. The little details add up to a tool that just works.
Final Thoughts
Building this taught me that sometimes you need to build the tool you wish existed.
Could I have used an existing converter? Sure. But none of them handled my use cases properly. So I built one that does.
The result is something I use daily, and I'm genuinely proud of how it turned out. From the smooth animations to the smart paste handling to the clean Markdown output - every detail was intentional.
This is what happens when you care about craft.
Not just making something that works, but making something that feels good to use. Something fast, beautiful, and reliable.
That's what I built. And honestly? I think I nailed it.
Built with ⚡ and way too much attention to detail
Want to see the code? Have feedback? Found a bug? I'm all ears. This is v1.0, and it's only getting better from here.
Now go convert something and see for yourself.
Which of these new features are you most excited to use?
— — — — — — — — — — — — — — — — — — — — — — —
Did you learn something good today?
Then show some love. 🫰
© Muhammad Usman
WordPress Developer | Website Strategist | SEO Specialist
Don’t forget to subscribe to Developer’s Journey to show your support.
Available for freelance projects on Upwork
Top comments (1)
I want you to test the tool and let me know if there are any bugs in tool