Hi folks! Some of you may have been following (or even using!) @nytimes/react-prosemirror
. Over a year ago now, I posted about exploring a new approach to integrating React and ProseMirror. This new approach has been available on NPM as @nytimes/react-prosemirror@next
for quite a while, and an increasingly large number of users have used it as their primary mechanism for integrating React and ProseMirror, including some large production services.
Unfortunately, the New York Times itself is still stuck on v1 of @nytimes/react-prosemirror
, and it may be a while before they’re able to upgrade. In the meantime, since I no longer work at the Times, my own needs for the library have diverged a bit from theirs. So last week we agreed that my software collective, Handle with Care, would take over the development of the v2 approach to React ProseMirror. The code now lives on GitHub at handlewithcarecollective/react-prosemirror, and v2 is officially published to NPM as @handlewithcare/react-prosemirror.
What’s new in v2?
Previously, React ProseMirror relied on ProseMirror’s EditorView to manage the DOM for the editor. To integrate it with React, we used React portals to render components into ProseMirror-managed DOM nodes, and a useLayoutEffect to sync state updates with the EditorView instance.
This approach provided some challenges. First, portals have to be rendered into existing, stable DOM nodes, so all React-based custom node views ended up wrapped in extra HTML elements. This made styling and producing valid DOM more challenging than it should be, and introduced corner cases in browser contenteditable implementations. Second, we induced a double render for every ProseMirror update, and during the first render, React-based custom node views will be rendered with the previous state. This is technically another form of the state tearing that this library was designed to prevent, as all other React components will be rendered with the new state!
To overcome these challenges, the new release moves rendering responsibility entirely into React. We disabled the EditorView’s DOM update cycle, and implemented the same update algorithm that prosemirror-view uses with React components. The result is a more idiomatic, React-based library, which doesn’t have any of the issues of the original implementation.
API changes
- The
ProseMirror
component API has changed slightly:- Instead of passing a
mount
prop with a ref to a child element, users must render aProseMirrorDoc
component as a child of theProseMirror
component. - The
nodeViews
prop no longer matches the ProseMirror API. Instead,nodeViews
should be a map from node type name to React components, each of which must takeNodeViewComponentProps
as props. This map should be memoized, or defined outside the React component entirely. - To pass standard ProseMirror node view constructors, use the
customNodeViews
prop
- Instead of passing a
- The API that React-based node views must implement has changed:
- There is no longer any need to provide a ProseMirror node view constructor function. React-based node views are now just React components that accept
NodeViewComponentProps
as props. - Props related to the ProseMirror node, such as the node itself, the
getPos
function, and decorations, now live in thenodeProps
property. All other props must be spread onto the root element of the node view component, aside fromchildren
, which may be rendered anywhere in the component, as appropriate. - All node view components must forward their ref to the root element of the component.
- To implement features that would normally live in the node view spec, there are new hooks, such as
useStopEvent
anduseSelectNode
- There is no longer any need to provide a ProseMirror node view constructor function. React-based node views are now just React components that accept
- There is a new export,
widget
, which behaves similarly toDecoration.widget
fromprosemirror-view
, but takes a React component instead of atoDOM
method.
Other fun things
@handlewithcare/react-prosemirror
supports SSR! If you like, you can server-side render your ProseMirror components. This can be particularly useful if you’d like to render a read-only version of your documents for search indexers or logged out users.@handlewithcare/react-prosemirror
supports React Strict Mode/Concurrent Mode, and it’s really fast, even for gigantic documents!