Build a Markdown Editor: A Beginner Project
SkillVeris Team
Engineering Team

A Markdown editor is a split-pane app: you type Markdown in a textarea and see rendered HTML update live in a preview panel.
In this guide, you'll learn:
- The core is a parser that turns Markdown syntax into HTML — you can use a library like marked or write a small one yourself.
- Listen to the textarea's input event and re-render the preview on every keystroke.
- Always sanitize the generated HTML with a tool like DOMPurify to prevent cross-site scripting.
- localStorage persists the document between page reloads with no backend needed.
1What a Markdown Editor Does
A Markdown editor lets you write in Markdown — a lightweight plain-text syntax where # means a heading and **bold** means bold — and shows a live HTML preview of the formatted result. You build it by connecting a textarea to a preview element and converting the text to HTML whenever it changes.
It is one of the best beginner projects because it combines several fundamentals: capturing input events, parsing and transforming strings, updating the DOM efficiently, and handling security correctly. You can build a working version in an afternoon and keep extending it for weeks.
2How the Editor Works
The editor runs a simple pipeline on every keystroke: read the raw Markdown, convert it to an HTML string, sanitize that HTML, then inject it into the preview panel.
- The user types Markdown into a textarea on the left.
- An input event listener fires on every change.
- A parser converts the Markdown string into HTML.
- A sanitizer strips any dangerous tags or attributes.
- The clean HTML replaces the preview panel's contents.
🔑Key Idea
The preview is a pure function of the textarea's value. Re-render from scratch on each change rather than trying to patch the DOM — it is simpler and fast enough for documents of any realistic size.
3Setting Up the Layout
Use CSS flexbox or grid to place the textarea and preview side by side, each taking half the width and the full height of the viewport. A monospace font in the editor and a normal font in the preview makes the two panes feel distinct.
Give both panes overflow-y: auto so long documents scroll independently. On narrow screens, stack them vertically with a media query so the editor stays usable on mobile.
- .container { display: flex; height: 100vh; }
- textarea { flex: 1; font-family: monospace; padding: 1rem; }
- #preview { flex: 1; padding: 1rem; overflow-y: auto; }
- @media (max-width: 600px) { .container { flex-direction: column; } }
4Converting Markdown to HTML
The heart of the project is conversion. For a production-quality result, use a well-tested library such as marked, which handles the full CommonMark specification including nested lists, code blocks, and tables. Include it and call marked.parse(text) to get HTML.
If you want to learn how parsing works, write a mini-parser that handles a handful of rules with regular expressions and string replacement. It will not cover every edge case, but building it clarifies what a real parser does under the hood.
A Minimal Hand-Rolled Parser
A few regex replacements cover headings, bold, italic, and inline code — enough to see the concept.
html = text.replace(/^# (.*)$/gm, '<h1>$1</h1>');
html = html.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>');
html = html.replace(/\*(.*?)\*/g, '<em>$1</em>');
html = html.replace(/`(.*?)`/g, '<code>$1</code>');5Wiring the Live Preview
Attach an input listener to the textarea so the preview updates as the user types. The input event fires on every character, paste, and delete, which is exactly what you want for a live preview.
For very large documents you can debounce the render so it runs at most every 100 milliseconds, but for typical notes the direct approach is smooth and simpler.
- const editor = document.getElementById('editor');
- const preview = document.getElementById('preview');
- editor.addEventListener('input', () => {
- const dirty = marked.parse(editor.value);
- preview.innerHTML = DOMPurify.sanitize(dirty);
- });
6Sanitizing the Output
Setting innerHTML with user-controlled content is a classic cross-site scripting (XSS) risk. If someone types a script tag or an onerror attribute into the editor, an unsanitized preview would execute it. Never trust the parser's output directly.
Run the HTML through a sanitizer like DOMPurify before inserting it. DOMPurify removes dangerous tags and attributes while keeping legitimate formatting intact. This one step is the difference between a safe editor and a security hole.
⚠️Security Essential
Markdown allows raw HTML by design, so a malicious note can contain <script> or event handlers. Always sanitize before assigning to innerHTML — this is not optional.
7Saving Work With localStorage
Users expect their text to survive a refresh. localStorage gives you a simple key-value store in the browser that persists across sessions with no backend. Save the editor content on every input and restore it when the page loads.
Because localStorage is synchronous and limited to a few megabytes, it is perfect for a single document. For multiple documents you would move to IndexedDB, but start simple.
- editor.addEventListener('input', () => localStorage.setItem('doc', editor.value));
- window.addEventListener('load', () => {
- editor.value = localStorage.getItem('doc') || '';
- editor.dispatchEvent(new Event('input'));
- });
8Common Mistakes to Avoid
A few pitfalls trip up nearly everyone building their first Markdown editor.
- Skipping sanitization — this opens an XSS hole the moment anyone types HTML.
- Rendering on the wrong event — use input, not keyup, so paste and cut are captured.
- Rebuilding the whole DOM manually instead of assigning the HTML string once.
- Forgetting to restore saved content on load, so the localStorage value looks broken.
- Writing a hand-rolled parser and expecting it to match CommonMark — use a library for real use.
9Key Takeaways
The project is a compact tour of front-end fundamentals.
- The preview is a function of the textarea value — re-render on every input event.
- Use marked (or a mini regex parser to learn) to convert Markdown to HTML.
- Always sanitize with DOMPurify before setting innerHTML to prevent XSS.
- localStorage persists the document across reloads without a backend.
- Flexbox gives you a responsive split-pane layout in a few lines of CSS.
10Frequently Asked Questions
Q: Should I write my own Markdown parser or use a library? A: For anything real, use a maintained library like marked or markdown-it — they handle the full spec and edge cases. Writing a small parser is a great learning exercise but should not power a production editor.
Q: Why do I need to sanitize the HTML? A: Markdown permits raw HTML, so a user could inject a script tag or event handler. Assigning that to innerHTML would run it. DOMPurify strips dangerous content while keeping valid formatting, preventing cross-site scripting.
Q: How do I stop the preview from lagging on large documents? A: Debounce the render so it runs at most every 100 milliseconds instead of on every keystroke. For most note-sized documents, direct rendering is already fast enough.
Q: Can I save more than one document? A: localStorage works well for a single document. For multiple files, switch to IndexedDB, which supports structured storage and larger capacity while still running entirely in the browser.
Related Reading
Get The Print Version
Download a PDF of this article for offline reading.
About the Publisher
SkillVeris Team
Engineering Team
Our engineering team documents real build journeys so you can learn by doing, not just reading.
View all postsRelated Posts
Never miss an update
Get the latest tutorials and guides delivered to your inbox.
No spam. Unsubscribe anytime.