HTML contenteditable Attribute
The contenteditable attribute specifies whether an element's content is editable. Learn its values, JavaScript handling, and accessibility tips.
The HTML contenteditable attribute specifies whether or not the content of an element is editable directly in the browser. When you turn it on, the user can click inside the element and type, delete, and format text just like in a word processor — no <input> or <textarea> required.
Because it works on almost any element, contenteditable can turn a <div>, a <p>, or a whole section into an editing surface. It is part of the Global Attributes, so it is available on every HTML element.
When to use contenteditable
contenteditable is the foundation of in-page editing. You will see it behind:
- Rich-text editors (the "WYSIWYG" boxes in CMSes, email clients, and comment forms). Unlike a
<textarea>, an editable element can hold real HTML — bold text, links, lists, and images — so the user sees formatted output as they type. - Inline editing — letting a user click a heading or table cell to rename it in place, instead of opening a separate form.
- Live-preview and note-taking UIs, where the editing area is the rendered result.
Choose a <textarea> or <input> when you only need plain text that submits with a form: they are real form fields, validate, and post their value automatically. Reach for contenteditable when you need formatted (HTML) content or want the edit to happen inside existing page layout.
Syntax
<tag contenteditable="true">...</tag>The attribute accepts the following values:
| Value | Meaning |
|---|---|
true (or "") | The element is editable. An empty string behaves the same as true. |
false | The element is not editable. |
inherit | The element takes its editable state from its nearest parent. This is also the default when the attribute is omitted. |
plaintext-only | The element is editable but rich-text formatting is disabled — only plain text is accepted. Supported in modern browsers, but check support before relying on it. |
Example
<!DOCTYPE html>
<html>
<head>
<title>Title of the document</title>
</head>
<body>
<p contenteditable="false">
This is a paragraph. It is not editable.
</p>
<p contenteditable="true">
This is a paragraph. It is editable. Try to change this text.
</p>
</body>
</html>Inheritance with the inherit value
When you do not set contenteditable on a child, it inherits the editable state of its parent. You can make a whole region editable on the parent and then opt a specific child back out with contenteditable="false":
<!DOCTYPE html>
<html>
<head>
<title>contenteditable inherit example</title>
</head>
<body>
<div contenteditable="true">
<p>This paragraph inherits editing from the div, so you can change it.</p>
<p contenteditable="false">
This paragraph opts out — it is locked and cannot be edited.
</p>
<p contenteditable="inherit">
This one explicitly inherits, so it is editable again.
</p>
</div>
</body>
</html>Reading and saving edited content with JavaScript
A contenteditable element is not a form field, so its value is not submitted with a form and there is no value property. Instead you read the content directly from the element:
element.innerHTML— the edited content as HTML (keeps bold, links, lists).element.textContent— the edited content as plain text (formatting stripped).
To react to edits as they happen, listen for the input event, which fires on every change:
<!DOCTYPE html>
<html>
<head>
<title>Save editable content</title>
</head>
<body>
<div id="editor" contenteditable="true">
Edit me, then reload the page.
</div>
<script>
const editor = document.getElementById("editor");
// Restore any previously saved content.
const saved = localStorage.getItem("note");
if (saved !== null) {
editor.innerHTML = saved;
}
// Save on every edit.
editor.addEventListener("input", () => {
localStorage.setItem("note", editor.innerHTML);
// In a real app you would debounce this and POST it to a server, e.g.
// fetch("/api/save", { method: "POST", body: editor.innerHTML });
});
</script>
</body>
</html>Saving innerHTML straight to a server stores raw HTML. Always sanitize untrusted HTML on the server (or with a vetted client library) before saving or re-displaying it, to prevent cross-site scripting (XSS).
Accessibility
A plain contenteditable element looks editable but, unlike a real form field, it exposes no field semantics to assistive technology — screen readers may not announce it as something the user can type into. When you build a custom editor, help them out:
- Add
role="textbox"so it is announced as an editable text field. Addaria-multiline="true"if it accepts multiple lines. - Add an
aria-label(or associate a visible<label>viaaria-labelledby) so the field has an accessible name. - Add
tabindex="0"if the element is not naturally focusable, so keyboard users can reach it.
<div
contenteditable="true"
role="textbox"
aria-multiline="true"
aria-label="Comment"
tabindex="0"
>
Type your comment…
</div>Browser inconsistencies
contenteditable is powerful but its behavior is not uniform across browsers — this is the main reason production apps usually build on a library (or use plaintext-only) rather than raw contenteditable:
- Enter key markup differs. Pressing Enter may wrap a new line in a
<div>, a<p>, or insert a<br>, depending on the browser. Don't assume the generated HTML structure. - Pasting often brings along styles and tags from the source; you may need to intercept the
pasteevent and clean it up. plaintext-onlydisables rich formatting but is not supported everywhere — feature-detect before relying on it.
Related attributes
spellcheck— toggle the browser's spell-checking inside an editable region.draggable— control whether an element can be dragged.- Global Attributes — the full set of attributes usable on any element.