← Back to DevBytes

How to Build a Markdown Editor with React

What Is a Markdown Editor?

A Markdown editor is a text input tool that allows users to write content using Markdown syntaxβ€”a lightweight markup language with plain-text formattingβ€”and instantly see a rendered HTML preview. Unlike traditional rich-text editors, Markdown editors keep the raw syntax visible while displaying the formatted output side by side (or inline), giving writers full control over their content structure without hiding the underlying markup.

Building one with React combines two powerful ideas: controlled components for real-time state management and markdown-to-HTML parsing for live previews. The result is a fast, interactive writing environment that runs entirely in the browser.

Why Build One with React?

Project Setup

πŸš€ Deploy your AI agent in 10 minutes

Managed Hermes hosting. Zero DevOps. 100M tokens/mo included.

Try it free →

Create a new React project using Vite (fast and minimal) or Create React App. The following steps assume Vite, but the component code works identically in any React setup.

npm create vite@latest markdown-editor -- --template react
cd markdown-editor
npm install

Next, install the marked libraryβ€”a battle-tested Markdown parser that converts raw Markdown strings into sanitized HTML. You'll also install DOMPurify to prevent XSS attacks from untrusted Markdown input.

npm install marked dompurify
npm install -D @types/dompurify

The project structure will be minimal. You'll create a single MarkdownEditor component and use it inside App.jsx.

src/
  β”œβ”€β”€ App.jsx
  β”œβ”€β”€ App.css
  β”œβ”€β”€ MarkdownEditor.jsx
  β”œβ”€β”€ MarkdownEditor.css
  └── main.jsx

Building the Core Component

The editor consists of two main panels: a raw Markdown textarea on the left and an HTML preview div on the right. Both are driven by a single piece of stateβ€”the Markdown string. When the user types, React updates state, which triggers the parser, which updates the preview.

Step 1: Create the MarkdownEditor Component

Create MarkdownEditor.jsx with the following structure. Notice how the useState hook holds the Markdown text, and useMemo caches the parsed HTML so it only recomputes when the text changes.

import { useState, useMemo } from 'react';
import { marked } from 'marked';
import DOMPurify from 'dompurify';
import './MarkdownEditor.css';

export default function MarkdownEditor() {
  const [markdown, setMarkdown] = useState(() => {
    // Default starter text so the editor isn't empty
    return `# Welcome to Your Markdown Editor

## Getting Started

This is a **live** preview editor. Type Markdown on the left and see the rendered HTML on the right.

### Features

- Real-time preview
- Syntax you already know
- Clean, minimal interface

> "The best writing tool is the one you actually use."

\`\`\`javascript
// Try inline code blocks
const greet = (name) => \`Hello, \${name}!\`;
console.log(greet('World'));
\`\`\`

Enjoy writing! ✨
`;
  });

  // Parse markdown and sanitize the resulting HTML
  const html = useMemo(() => {
    const rawHtml = marked.parse(markdown, { breaks: true, gfm: true });
    return DOMPurify.sanitize(rawHtml);
  }, [markdown]);

  const handleChange = (e) => {
    setMarkdown(e.target.value);
  };

  return (
    

Markdown