Make an HTML element editable with the contenteditable attribute

You can make the contents of an HTML element editable, much like the text in a form field, by adding the contenteditable attribute.

<div id="editable-div" contenteditable="true">Hello!</div>

You can edit that div 👇

Hello!
Value: Hello!

contenteditable is an enumerated attribute rather than a Boolean attribute. It accepts these values:

  • true or an empty value makes the element editable
  • false makes the element non-editable
  • plaintext-only allows editing but disables rich-text formatting

If the attribute is missing or has an invalid value, its value is inherited from the parent. With contenteditable="true", pasted formatting is retained; with contenteditable="plaintext-only", the browser removes it.

Listening for changes on a contenteditable element

A contenteditable element has no value property, and the change event used with form fields is not the right event here. Listen for the input event and read the element's text instead:

const editableDiv = document.getElementById('editable-div')
 
if (editableDiv === null) {
  throw new Error('Editable div not found')
}
 
editableDiv.addEventListener('input', event => {
  console.log(event.currentTarget.innerText)
})

Notice that getElementById() receives editable-div, without the # used in a CSS selector. Use innerText when you want the rendered plain text. Use innerHTML only when you intentionally need the element's markup.

The related beforeinput event fires before the browser changes the DOM. It is useful when an editor needs to inspect or override an edit.

Older examples often use document.execCommand() for formatting and clipboard operations. The method is deprecated and inconsistently implemented, so do not build a new editor around it. For clipboard operations, use the Clipboard API.

A contenteditable React component

React warns when a contentEditable element also has children managed by React:

Error message from JavaScript console
Warning: A component is `contentEditable` and contains `children` managed by React. It is now your responsibility to guarantee that none of those nodes are unexpectedly modified or duplicated. This is probably not intentional.

The warning is correct: Hello! is a child, even though it is only a text node. The browser can modify that child while React assumes it owns the same DOM.

For a small plain-text editor, you can deliberately leave the editable DOM uncontrolled, mirror its text into state, and suppress the warning:

import { useState } from 'react'
 
function EditableDiv() {
  const [divText, setDivText] = useState('Hello!')
 
  return (
    <>
      <div
        aria-label='Editable text'
        aria-multiline='true'
        className='pink-div'
        contentEditable='plaintext-only'
        onInput={event => setDivText(event.currentTarget.innerText)}
        role='textbox'
        suppressContentEditableWarning
      >
        Hello!
      </div>
      <div className='value-container'>
        <strong>Value:</strong> {divText}
      </div>
    </>
  )
}

This example displays the current text elsewhere, but it does not render divText back into the editable element. Replacing the element's children on every keystroke can reset the caret or selection. A full rich-text editor also has to manage selections, undo history, pasted content, and browser differences, so a tested editor library is usually a better starting point.

Accessibility, forms, and security

Prefer an <input> or <textarea> when you only need a normal text field. They provide built-in semantics, validation, and form submission. A contenteditable element is focusable, but it is not a form control and its contents are not submitted automatically.

If it acts as a text field, give it an accessible name and role="textbox". Add aria-multiline="true" when Enter inserts a new line, as in the React example above.

Finally, treat editable HTML as untrusted input. Rich content can contain markup introduced through pasting or editing, so sanitize it before saving or rendering it. plaintext-only is the safer choice when formatting is unnecessary, but it does not replace server-side validation.

Conclusions

contenteditable is useful for inline renaming, editable demos, and as a building block for rich-text editors. For ordinary plain-text input, native form controls are simpler and more robust.