Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/ponymessenger/use-prosemirror

ProseMirror + React made easy
https://github.com/ponymessenger/use-prosemirror

hooks-api-react javascript prosemirror react react-hook react-hooks reactjs rich-text-editor typescript wysiwyg-editor wysiwym

Last synced: 3 months ago
JSON representation

ProseMirror + React made easy

Awesome Lists containing this project

README

        

# use-prosemirror

_ProseMirror + React made easy_

**NOTE**: This library is in production at [Pony
Messenger](https://www.ponymessenger.com/). It was open
sourced to contribute to the incredible ProseMirror
ecosystem, and to ensure the best possible experience for
Pony users.

- [Example](#example)
- [Installation](#installation)
- [Usage](#usage)
- [`useProseMirror(config)`](#useprosemirrorconfig)
- [``](#prosemirror-)
- [More Info](#more-info)

[ProseMirror](https://prosemirror.net/) is one of the best rich
text editors out there. Although it is not written in React, its
render model is very similar to React's. It is therefore a great
fit for your React project.

This package lets you bootstrap a minimal, unopinionated React
integration quickly, using modern React best practices.

Unlike other integrations, it:

- Separates state and presentation so you can keep your state
as high up as necessary.
- Allows using [`EditorView` props](https://prosemirror.net/docs/ref/#view.Props)
as React props.
- Is written in TypeScript.

## Example

A simple example is available on
[CodeSandbox](https://codesandbox.io/s/use-prosemirror-basic-example-nie7f?file=/src/App.tsx).
Please join [this discussion](https://github.com/dminkovsky/use-prosemirror/discussions/8) if
you are interested in further usage examples.

## Installation

```
npm install --save use-prosemirror
```

or

```
yarn add use-prosemirror
```

This package specifies React and ProseMirror as peer dependencies,
so make sure you have them installed, too.

## Usage

This is all you need to get started:

```javascript
import 'prosemirror-view/style/prosemirror.css';

import React from 'react';
import {schema} from 'prosemirror-schema-basic';
import {useProseMirror, ProseMirror} from 'use-prosemirror';

export function MyEditor() {
const [state, setState] = useProseMirror({schema});
return ;
};
```

### `useProseMirror(config)`

This hook maintains editor state. It accepts one argument: the
[same object as
`EditorState.create`](https://prosemirror.net/docs/ref/#state.EditorState%5Ecreate).
It follows the same convention as React's `useState` and creates
the initial state and returns it along with an update function.

### ``

This component wraps ProseMirror's `EditorView`. It displays the
editor state provided by `useProseMirror()` and dispatches state
updates using the update function. It accepts the following props:

- `state` — the `EditorState` created by `useProseMirror`.
- `onChange` — a function that accepts the next state. This can be
the update function returned by `useProseMirror`, or some function
that accepts the next state and eventually calls the update
function. Provide this if you do not provide
[`dispatchTransaction`](https://prosemirror.net/docs/ref/#view.DirectEditorProps.dispatchTransaction).
- `style` — (optional) a React style object to pass to the `div` containing ProseMirror.
- `className` — (optional) a string of classes you want to pass to the `div` containing ProseMirror.
- `editorViewFactory` — (optional) a function that accepts the root
DOM node where the editor will be mounted, `DirectEditorProps` and
component props, and returns an `EditorView`. Use this if you extend
`EditorView` or want to instantiate the `EditorView` in some special way.

It also accepts any
[`EditorProps`](https://prosemirror.net/docs/ref/#view.EditorProps).
So you can, for example, set
[`transformPastedHTML`](https://prosemirror.net/docs/ref/#view.EditorProps.transformPastedHTML)
directly on the component:

```javascript
{
console.log('transformPastedHTML', string);
return string;
}}
/>
```

**Note**: If passing a value for the `nodeViews` prop, its identity
should be stable. If it changes on every render, the editor will
not work correctly. This issue is described in
[#1](https://github.com/dminkovsky/use-prosemirror/issues/1#issue-696086919)
and an example of using `nodeViews` with React is given in
[#5](https://github.com/dminkovsky/use-prosemirror/issues/5#issuecomment-769510937).

If you pass
[`dispatchTransaction`](https://prosemirror.net/docs/ref/#view.DirectEditorProps.dispatchTransaction)
to ``, you are responsible for applying dispatched
transactions to the existing state and calling the update function
returned by `useProseMirror()`. `dispatchTransaction` takes
precedence over `onChange`, which will not be called if
`dispatchTransaction` is provided.

If you pass a `ref`, `` exposes a `view` getter to retrieve the underlying [`EditorView`](https://prosemirror.net/docs/ref/#view.EditorView) instance:

```javascript
const [state, setState] = useProseMirror({schema});
const viewRef = useRef();
return (
{
setState(state);
console.log(viewRef.current.view.hasFocus());
}}
/>
);
```

## More Info

React and ProseMirror follow the same data flow model. Even though
ProseMirror is not written in React, it fits nicely into a React
project.

From the [ProseMirror documentation](https://prosemirror.net/docs/guide/#view):

> So the editor view displays a given editor state, and when
> something happens, it creates a transaction and broadcasts this.
> This transaction is then, typically, used to create a new state,
> which is given to the view using its updateState method.
>
>
>
> This creates a straightforward, cyclic data flow, as opposed to
> the classic approach (in the JavaScript world) of a host of
> imperative event handlers, which tends to create a much more
> complex web of data flows.

Sound familiar? It continues:

> It is possible to ‘intercept’ transactions as they are dispatched
> with the dispatchTransaction prop, in order to wire this cyclic
> data flow into a larger cycle—if your whole app is using a data
> flow model like this, as with Redux and similar architectures, you
> can integrate ProseMirror's transactions in your main
> action-dispatching cycle, and keep ProseMirror's state in your
> application ‘store’.

Which is exactly what [this module does](src/ProseMirror.tsx).