Raw WYSIWYG Demo

This editor uses the browser's built-in contentEditable and document.execCommand() — the oldest approach to rich text editing on the web.

Try the toolbar buttons above. Notice how:

  • Bold, italic, underline work as expected
  • The HTML output can be messy (nested spans, empty tags)
  • Undo/redo uses the browser's native stack
  • No schema validation — any HTML structure is allowed
This is how TinyMCE and early CKEditor worked. Modern editors (ProseMirror, Tiptap, Lexical) use contentEditable as a rendering layer but manage state independently.

Click the HTML button to see the raw HTML source and edit it directly.

How Traditional WYSIWYG Works

javascript
// The traditional WYSIWYG approach — document.execCommand
const editor = document.getElementById('editor');
editor.contentEditable = 'true';

// Format commands
document.execCommand('bold');           // Toggle bold
document.execCommand('italic');         // Toggle italic
document.execCommand('underline');      // Toggle underline
document.execCommand('insertUnorderedList'); // Create bullet list
document.execCommand('formatBlock', false, '<h2>'); // Heading
document.execCommand('createLink', false, 'https://example.com');

// ⚠️ execCommand is deprecated!
// It's still widely supported but:
// - No spec guarantee
// - Inconsistent across browsers
// - No undo/redo control
// - Produces messy HTML
// - No custom block types

Modern Approach

Modern editors still use contentEditable for rendering, but they manage the document state independently:

javascript
// Modern approach: ProseMirror/Tiptap/Lexical
// Instead of execCommand, modern editors use:

// 1. Immutable state + transactions
const tr = state.tr.addMark(from, to, schema.marks.bold.create());
view.dispatch(tr);

// 2. Schema-validated documents
// Only allowed structures can exist

// 3. Custom rendering
// Full control over how each node renders

// 4. Proper undo/redo via history plugin
// Not browser's native undo stack

The Evolution

2000s
execCommand era — TinyMCE, CKEditor 3/4. Direct DOM manipulation via browser APIs. Messy HTML, inconsistent cross-browser.
2015
ProseMirror — First editor with immutable state, schema-defined documents, and functional transactions. Revolutionary architecture.
2016
Slate, Draft.js — React-based editors with controlled state. Pioneered component-based editing.
2020+
Tiptap, Lexical, CodeMirror 6 — Modern era. Headless architectures, CRDT collaboration, framework-agnostic (mostly).

Strengths (of raw contentEditable)

  • Zero dependencies — built into every browser
  • Simple to get started
  • Free native undo/redo
  • Spell check, autocomplete for free

Weaknesses

  • execCommand is deprecated
  • Produces messy, unpredictable HTML
  • No document model or schema
  • Inconsistent across browsers
  • No collaboration support
  • Cannot define custom block types