# react-editable-div **Repository Path**: mirrors_svenanders/react-editable-div ## Basic Information - **Project Name**: react-editable-div - **Description**: React Component that enables editable divs - **Primary Language**: Unknown - **License**: ISC - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2020-08-18 - **Last Updated**: 2026-09-05 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # react-editable-div [![npm](https://img.shields.io/npm/v/react-editable-div.svg)](https://www.npmjs.com/package/react-editable-div) [![types](https://img.shields.io/npm/types/react-editable-div.svg)](https://www.npmjs.com/package/react-editable-div) [![license](https://img.shields.io/npm/l/react-editable-div.svg)](./LICENSE) A tiny, caret-safe `contenteditable` for React 18 and 19. Drop it in when you need an editable div — comments, titles, notes, inline labels — without pulling in a document editor. Typing updates parent state; the caret does not jump. Zero runtime dependencies. ```tsx import { useState } from "react"; import { Editable } from "react-editable-div"; import "react-editable-div/styles.css"; // optional placeholder styles export function Bio() { const [html, setHtml] = useState("Hello world"); return ; } ``` ```bash npm install react-editable-div ``` Peer dependency: `react >= 18`. ## Why this exists `contenteditable` and React collide. If you render `dangerouslySetInnerHTML` on every keystroke, the browser resets the caret. The 2014 version of this package avoided that by skipping updates entirely — and then silently dropped parent `html` changes. 1.0 keeps the caret still while you type, and still applies `html` when the parent changes it from the outside. | You want | Use | | --- | --- | | An editable div / heading / label | **react-editable-div** | | A document editor, toolbar, collab, markdown | TipTap, Lexical, or Slate | ## Features - Caret-safe controlled and uncontrolled modes - IME composition (CJK input is not flushed mid-keystroke) - `plaintextOnly` and `pasteAsPlainText` - Placeholder via `data-placeholder` (import the optional CSS) - Any `tagName` (`div`, `h1`, `article`, …) - Forwarded ref to the DOM node (`focus()`, `innerHTML`) - `"use client"` for Next.js App Router - TypeScript types included ## API `onChange(html, event)` — first argument is the current `innerHTML`. | Prop | Type | Default | Notes | | --- | --- | --- | --- | | `html` | `string` | — | Controlled value | | `defaultHtml` | `string` | `""` | Uncontrolled initial value | | `onChange` | `(html, event) => void` | — | Fired on `input`, `blur`, and `compositionend` | | `editable` | `boolean` | `true` | `false` makes it read-only | | `disabled` | `boolean` | `false` | Same as `editable={false}` | | `tagName` | `keyof HTMLElementTagNameMap` | `"div"` | Remounts the node if it changes | | `placeholder` | `string` | — | Shown when the element is empty | | `plaintextOnly` | `boolean` | `false` | `contenteditable="plaintext-only"` with a fallback | | `pasteAsPlainText` | `boolean` | `false` | Inserts `text/plain` on paste | | `sanitize` | `(html) => string` | — | Transforms the string passed to `onChange` | | `ref` | `Ref` | — | The editable element | All other native HTML attributes (`className`, `style`, `spellCheck`, `onKeyDown`, …) are forwarded. The element defaults to `role="textbox"` and `aria-multiline="true"`. When it is not editable, `aria-readonly="true"` is set. ### Sanitize and XSS This package does not sanitize HTML. If you persist or re-render `html` as markup, run it through [DOMPurify](https://github.com/cure53/DOMPurify) (or similar) on the way out. A `sanitize` prop that rewrites markup on every keystroke will jump the caret in controlled mode if the parent echoes the cleaned string back — sanitize on save, not on each input. ### Placeholder CSS ```ts import "react-editable-div/styles.css"; ``` Or copy: ```css [data-placeholder]:empty::before, [data-placeholder][data-empty]::before { content: attr(data-placeholder); color: color-mix(in srgb, currentColor 42%, transparent); pointer-events: none; } ``` Empty `contenteditable` nodes often contain a `
`, so `:empty` alone is not enough. The component toggles `data-empty` for that case. ## Demo ```bash npm install npm run demo ``` Opens a playground at `http://localhost:5173` — live HTML, editable toggle, plaintext mode, paste-as-text, and a “reset from parent” control to prove external updates. ## Migration from 0.0.4 ```diff - save(e.target.value)} /> + save(html)} /> - this.refs.editable.getDOMNode().innerHTML + ref.current.innerHTML ``` - React 18+ is required - `onChange` is `(html, event) => void`, not a mutated synthetic event - `disabled` is an alias of `editable={false}` - `html` is optional - CommonJS: `const { Editable } = require('react-editable-div')` (the module is also the component, matching 0.0.4) ## License ISC