Back to roadmaps react Course

Project: Markdown Live Editor

In this project, we will build a Markdown Live Editor. The application features a side-by-side split screen: a text area input pane on the left, and a rendered HTML preview pane on the right.


1. Safety Alert: dangerouslySetInnerHTML

To render raw HTML strings generated from Markdown compilation, React requires you to use the dangerouslySetInnerHTML attribute. This attribute name serves as a warning that rendering unescaped HTML can expose users to Cross-Site Scripting (XSS) vulnerabilities.

In production applications, always sanitize the HTML string using libraries like dompurify before passing it to React.


2. Implementing the Editor

Create a new file named MarkdownEditor.jsx and add the following implementation:

import { useState } from "react";

// Simple custom compiler parsing headers, bold text, and line breaks
function compileMarkdown(markdownText) {
  let html = markdownText;

  // 1. Parse headers (e.g., # Header -> <h1>Header</h1>)
  html = html.replace(/^# (.*$)/gim, "<h1>$1</h1>");
  html = html.replace(/^## (.*$)/gim, "<h2>$1</h2>");
  html = html.replace(/^### (.*$)/gim, "<h3>$1</h3>");

  // 2. Parse bold text (e.g., **bold** -> <strong>bold</strong>)
  html = html.replace(/\*\*(.*)\*\*/gim, "<strong>$1</strong>");

  // 3. Parse line breaks into paragraphs
  html = html.split("\n\n").map(para => {
    if (para.trim().startsWith("<h")) return para;
    return `<p>${para.replace(/\n/g, "<br />")}</p>`;
  }).join("");

  return html;
}

export function MarkdownEditor() {
  const [markdown, setMarkdown] = useState("# Live Editor\n\nType some **bold text** here...");

  const compiledHtml = compileMarkdown(markdown);

  return (
    <div className="editor-container">
      {/* Input Textarea panel */}
      <div className="editor-pane">
        <h3>Markdown Input</h3>
        <textarea
          value={markdown}
          onChange={(e) => setMarkdown(e.target.value)}
          placeholder="Write markdown syntax..."
        />
      </div>

      {/* Rendered HTML preview panel */}
      <div className="preview-pane">
        <h3>Live HTML Preview</h3>
        <div
          className="rendered-preview"
          dangerouslySetInnerHTML={{ __html: compiledHtml }}
        />
      </div>
    </div>
  );
}

3. Designing a Split-Screen Layout

Add the following split-screen CSS stylesheet to display the edit and preview panes side-by-side:

.editor-container {
  display: flex;
  gap: 20px;
  height: 500px;
}

.editor-pane, .preview-pane {
  flex: 1;
  display: flex;
  flex-direction: column;
}

textarea {
  flex: 1;
  padding: 15px;
  font-family: monospace;
  font-size: 14px;
  border: 1px solid #ccc;
  border-radius: 6px;
  resize: none;
}

.rendered-preview {
  flex: 1;
  padding: 15px;
  border: 1px solid #ccc;
  border-radius: 6px;
  background: #fafafa;
  overflow-y: auto;
}
Published on Last updated: