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

Click in the middle of this sentence and type a few letters.

60 characters in state

Seeded once

60 characters in state

The decision

Stop React from owning the field's content at all.

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.

All case studies