Skip to the solution
HardMachine Coding14 min read

Build a basic Rich Text Editor

135 views

The problem

Question presented to candidate: "Build a basic rich text editor for release notes: a toolbar with Bold, Italic, Underline, a bulleted list and a numbered list, plus Ctrl+B, Ctrl+I and Ctrl+U. Do not use document.execCommand, and the HTML it produces must be safe to store and show to other users."

Requirements:

  • A contenteditable area that starts with a few paragraphs, and a toolbar whose buttons show aria-pressed for the current selection.
  • Bold, Italic and Underline toggle on the selection; with only a caret, they format the word the caret is in.
  • A selection that is partly bold becomes fully bold; toggling again removes bold.
  • List buttons turn the selected blocks into list items, switch between list types, and turn them back into paragraphs.
  • The selection survives every command, and clicking a toolbar button does not steal it.
  • Paste inserts plain text only. The editor emits normalised HTML that contains only p, ul, ol, li, strong, em, u and br.

What a strong answer should cover:

  • Why execCommand is out: deprecated, inconsistent output across browsers, and no control over the markup.
  • A small document model (blocks of text runs with marks) with pure commands, and a parse step from the DOM and a render step back to HTML.
  • Mapping the DOM selection to model positions (block index and character offset) and restoring it after re-rendering.
  • Not letting the framework render children into the editable element, so typing and framework patches do not fight.
  • Sanitising by construction: parse keeps only known tags, and paste is plain text.

Clarifying questions expected:

  • "Which formats are needed, and is nesting such as lists inside lists in scope?"
  • "Should paste keep formatting from Word or Google Docs, or is plain text acceptable?"
  • "Do we need undo and redo beyond what the browser gives us?"

Code / implementation expected: Yes. The model, the commands and selection mapping, and the toolbar UI.

  • React
  • Editor
  • ContentEditable

Try it yourself

Sketch your approach before you read the solution. That is what the interview tests.

Stuck? Get a hint.

A short nudge toward the approach. It will not give the answer away.

Solution

The solution is hidden so you can try first

14 min read ยท 9 sections

React solution

Every file of the working solution. Open it in the playground to run and change it.

ReactComplete solution
import { useState } from "react";
import RichTextEditor from "./src/components/RichTextEditor";

const css = `
  body { margin: 0; background: #0f1115; color: #e6e6e6; font-family: system-ui, sans-serif; }
  .page { max-width: 680px; margin: 0 auto; padding: 20px 16px; }
  .rte { border: 1px solid #2c3340; border-radius: 10px; background: #171a21; overflow: hidden; }
  .rte-tools { display: flex; flex-wrap: wrap; gap: 4px; padding: 6px; border-bottom: 1px solid #2c3340; }
  .rte-tools button { background: #222834; color: #e6e6e6; border: 1px solid #2c3340; border-radius: 6px; padding: 4px 10px; min-width: 34px; cursor: pointer; font: 14px system-ui, sans-serif; }
  .rte-tools button[aria-pressed="true"] { background: #f9a825; color: #1c140a; border-color: #f9a825; }
  .t-b { font-weight: 700; } .t-i { font-style: italic; } .t-u { text-decoration: underline; }
  .rte-body { min-height: 140px; padding: 10px 14px; outline: none; line-height: 1.5; }
  .rte-body:focus-visible { box-shadow: inset 0 0 0 2px #f9a825; }
  .rte-body p { margin: 0 0 8px; }
  .out { margin-top: 12px; font: 12px ui-monospace, monospace; color: #9aa4b2; white-space: pre-wrap; word-break: break-word; }
`;

export default function App() {
  const [html, setHtml] = useState("");
  return (
    <main className="page">
      <style>{css}</style>
      <h1 style={{ fontSize: 18 }}>Write release notes</h1>
      <RichTextEditor onChange={setHtml} />
      <p className="out" data-testid="out">
        {html}
      </p>
    </main>
  );
}

Discussion

Other approaches, follow-ups and how it went in real interviews.

Sign in to join the discussion.

No comments yet. Share how you would answer it.

Question 105 of 107 in Machine coding

Keep your momentum going.

Mark this question solved to track your progress, then take the next one while it is fresh.

Up nextHard

Build @Mentions in a textarea

Continue