Frontend · React 19, TypeScript
An editable page that keeps the cursor where you left it
The problem
A team edits AI-generated pages in place. Headlines, paragraphs, table cells and list items are all editable text inside one React page. The page around those fields is busy: dirty flags, a live preview, autosave and section highlighting all update state while someone types.
What failed first
The obvious version is a controlled field. Render the HTML into a contentEditable element, put every input event into state, and render again.
It loses the cursor. On each keystroke React writes the new HTML back into the element. The browser replaces the text the cursor was sitting in, and the cursor jumps to the start of the field in the middle of a sentence.
Try both below. Click in the middle of each sentence and type.
Controlled by React
Seeded once
The decision
Stop React from owning the field's content at all.
- The element renders with no children, so a parent re-render can change its attributes but never its text.
- The starting HTML is written once, when the element mounts. It is captured in a ref, so a changed prop can never re-seed a field someone is typing in.
- Resetting a field is a remount, done by changing its React
key. "Reset this section" is a key change, not a data change. - Paste is forced to plain text, so formatting copied from other apps never enters a document that is later saved as Markdown.
The same rule holds one level up. The card component that wraps every field is defined at module scope. Defined inside its parent, it would be a brand-new component type on every render, React would remount it, and the cursor would be lost again from the other direction.
The result
A 1,565-line structured editor and a 1,948-line detail page both sit on this one field. The fix is structural rather than careful: there is no code path through which React writes into a field while someone is editing it.