HTMLEditable Content (contenteditable)

The contenteditable Attribute

The contenteditable attribute turns any element into something the user can click into and edit directly, right in the rendered page — no <textarea> required. It is the foundation many simple rich-text editors are built on.

Basic Usage

contenteditable-basic.html

HTML
<div contenteditable="true">
  This text can be clicked and edited in place.
</div>
  • contenteditable="true" (or just contenteditable) makes the element editable.

  • contenteditable="false" explicitly disables editing (useful to override an inherited editable state).

  • Without the attribute at all, most elements default to non-editable.

Inheritance

contenteditable is inherited by children unless they explicitly override it, which lets you make one part of a nested UI editable while carving out non-editable "islands" inside it.

contenteditable-inheritance.html

HTML
<div contenteditable="true">
  <p>You can edit this paragraph.</p>
  <p contenteditable="false">
    This one is locked, even though its parent is editable.
  </p>
</div>
Use Case: A Simple Rich-Text Editor

Combined with a toolbar and the older{' '} document.execCommand API (or, in modern code, direct DOM manipulation / the newer{' '} Selection and Range APIs), a{' '} contenteditable region becomes a lightweight editor.

mini-editor.html

HTML
<div id="toolbar">
  <button data-cmd="bold"><b>B</b></button>
  <button data-cmd="italic"><i>I</i></button>
</div>
<div id="editor" contenteditable="true">
  Start typing your note here...
</div>

<script>
  document.getElementById('toolbar').addEventListener('click', (event) => {
    const cmd = event.target.dataset.cmd
    if (cmd) {
      document.execCommand(cmd) // deprecated — see note below
    }
  })
</script>
execCommand Is Deprecated
Do not build new editors on execCommand
document.execCommand is marked obsolete in the HTML spec and behaves inconsistently across browsers. It still works today for quick demos, but production rich-text editors should use the modern Selection/Range APIs directly, or — far more commonly in practice — a maintained editor library (e.g. TipTap, Lexical, ProseMirror, Quill) built on those primitives.
Listening for Changes

The input event fires on a contenteditable{' '} element whenever its content changes, just like it does on form fields — useful for autosaving or live character counts.

contenteditable-input-event.js

JS
const editor = document.getElementById('editor')

editor.addEventListener('input', () => {
  console.log('Current content:', editor.innerHTML)
})
Current content: Start typing your note here...
Current content: Start typing your note here! Edited.
Security: Sanitize Before Saving
contenteditable output is a major XSS risk
editor.innerHTML can contain anything the user typed, pasted, or dragged in — including <script> tags, event-handler attributes, or malicious links. If you save this HTML and later render it for other users (or even the same user), you must sanitize it on the server (or with a trusted client-side sanitizer like DOMPurify) before it is ever rendered again.
  • Never store raw innerHTML from a contenteditable region and re-render it unsanitized elsewhere.

  • Prefer a strict allow-list sanitizer over trying to blacklist "bad" tags yourself.

  • Sanitize again on the server even if you also sanitize on the client — client-side checks can be bypassed.

Accessibility Considerations
  • A contenteditable region should usually have role="textbox" and, if multi-line, aria-multiline="true" for screen reader users.

  • Provide a visible focus style — editable regions are easy to miss without one.

  • For most simple forms, a real <textarea> is more accessible and far less code than a custom contenteditable editor — reach for contenteditable when you specifically need rich formatting.

Prefer a library for anything non-trivial
Building a robust rich-text editor from scratch on contenteditable involves surprisingly gnarly edge cases (undo/redo, paste handling, cursor position across browsers). For anything beyond a toy example, a maintained editor framework will save significant time.
Quick Reference

Attribute value

Effect

contenteditable="true" / contenteditable

Element becomes editable

contenteditable="false"

Element is explicitly not editable

(attribute absent)

Not editable, and does not inherit editability