Draft.js – Rich Text Editor Framework for React
facebook.github.io
facebook.github.io
[1]: https://prosemirror.net [2]: http://marijnhaverbeke.nl/blog/collaborative-editing.html
We use CM at Overleaf[1], and it's been fantastic. To make it easier for non-LaTeX users to collaborate on LaTeX documents, we built our own rich text editor on top of it[2], which is still in beta. Still got a long way to go!
But, then again, shouldn't a single editor be able to handle those two usage-domains? (As this would mean the editor would be more powerful in terms of extensibility).
ProseMirror's model is structured. It's a nesting of elements strictly conforming to a configurable schema (by default close in spirit to Markdown - paragraphs/lists, emphasis, links, etc). Note that it's not "flat WYSIWYG" where each character/line has a style; a sub-list is actually inside the parent list, not just a bullet which happens to have higher indentation.
CodeMirror was started in early 2007. [1] ProseMirror is newer, announced July 7 2015 [2]. You've probably been using CodeMirror but didn't even realize it -- it's ubiquitous!
>CodeMirror is an open-source project shared under an MIT license. It is the editor used in the dev tools for both Firefox and Chrome, Light Table, Adobe Brackets, Bitbucket, and many other projects [3].
Check out the CodeMirror JavaScript, HTML and Markup code folding demo -- who could say no to that? [4]
[1] https://en.wikipedia.org/wiki/CodeMirror
[2] http://marijnhaverbeke.nl/blog/prosemirror.html
What was so cool recently was that a group of 7th/8th graders[1] used us to write their 300-page engineering notebook!! Just amazing :)
[1] https://www.overleaf.com/blog/289-the-nano-ninjas-building-r...
Imperative components with APIs is where real reusability exists. Case in point: React can consume them but cannot create them for external consumption.
EDIT: I hate to make this reply self-serving, but this is exactly why domvm [1] now exists. It promotes imperative components with declarative OR imperative view/subview composition. It slots firmly in between Mithril and pure-vdom frameworks, removing structural enforcement and avoiding promotion of a specific MV* paradigm. It's among the most performant vdom implementations to boot.
At least, that's how I operate.
If your component is React all the way down, it will be anything but trivial to extract it and could amount to a full rewrite. In reality this is true for whichever view layer you end up using (domvm included). The difference comes down to the structural flexibility the view layer affords you, speed and the weight it adds to your app code. domvm is 9k min for the view core or 13k min w/mutation observers, isomorphism and router.
In some sense, a jQuery component is much more reusable but slower than any view layer and the most ad-hoc. While there's still a dependency, there is not a jQuery-imposed structural dependency since most of what you're doing with jQuery is manipulating the DOM with some sugar.
When you write a library, you need to think very carefully about the deps you bring because you'll be imposing it to all your users.
The users of the released library inherit whatever dependencies that weren't easily removeable.
Where it falls down is mostly in documentation, which is 100% understandable, since it was crowdfunded(!), but the code is very legible and amenable to most kinds of webapps.
Where i suspect draft.js probably shines is having tighter client/data boundaries and code that natively expects to be rendered isomorphically, so calls to window/document are probably more tightly managed given that the whole view layer uses react and likely expects to be rendered on the server.
That said, ProseMirror has a 100% fantastic "give me the current ast rendered as markdown" which, in my case at least, was the thing i wanted the most and that ProseMirror supplied handily. Draft.js will have you roll your own[1], but at least the draft.js api is fairly friendly.
There's obviously more than enough room for both projects, but i think that for react based projects, draft.js will feel like a more natural fit even if it will take more effort in the short term until more functionality builds up around it.
[1]: https://github.com/facebook/draft-js/blob/06891490069d3ccfb8...
Also, the very first thing in their documentation is an explanation of how to write a 'card' for embedded input, which is what is used to embed images in the demo.
It was an ah-ha! moment for me because most editor documentation begins by telling you how they parse ranges, and showing you how you can use regex to back-check to document and change it to your own elements, etc. That's just too deep into the internals for me to get an idea of what's going on.
The docs back then (and even now) do this thing where they get rapidly tautological and have you hop around between type definitions but don't, for instance, show you what the default `menuBar`'s content value is, in terms of a MenuGroup so it's not readily obvious how to customize my own without reading the source.
So reading the docs, while i can certainly find out what things are and how they connect, i don't have a good sense of what decisions prosemirror made for me (or how it made them) so that i can intentionally customize or integrate in a non-backwards way. If the code weren't so legible, this would be a massive problem, but it's the sort of in-depth documentation that needs somebody working strictly on documentation in that way that kaplan-moss talks about[2]. I totally get that prosemirror is early days and integrating it has been a good experience, but that's the sort of documentation pain-point i was referring to. Thanks again!
[1]: http://prosemirror.net/ref.html#menu/menubar [2]: https://jacobian.org/writing/great-documentation/
Yes, but you are Marijn Haverbeke and they are not. I have more faith in your ability to make good decisions than I have in any giant tech company, TBH.
But apart from that, CM works like a charm out of the box, so thanks a ton for it :)
This is the type of cool stuff you get to work on at Facebook, come work with us :-)
We'll see how this plays out.
Could a reason be that Google Docs allows for offline editing (if you install the Chrome extension), making it easier to save all these changes at once when an internet connection returns? You mention later that OT doesn't help when trying to merge conflicting edits (in which case Google Docs will spawn a "<collaborator>'s conflicting copy of <document name>", IIRC). However, if this level of conflicting desynchronisation is rare, and if OT makes it easy to push large changes after editing the document offline for a while, they might have decided it was worth it.
The past year or so has really seen a resurgence of browser-based text editors. Off the top of my head I can think of 4 editors/editor-frameworks launched in the past year (Mobiledoc-Kit, ProseMirror, Trix, and now Draft.js). Browsers are creeping toward exposing all the events that are necessary for interpreting the meaning of a user as they input text. (Some notable exceptions remain, such as an event that would be fired when a spelling correction is accepted, but mutation observers provide a fallback for cases where it's not otherwise possible to catch the input on the way in.)
A major focus of Mobiledoc-Kit, which seems to have some overlap with Draft.js, is on exposing an API that allows programmers to programmatically interact with the internal (structured) document. Our goal is to allow developers to be able to construct editors that fit snugly fit their use case, whether that's building their own UI for a toolbar, or more complex procedural rules for document (e.g., add a constraint that there can only be one "H1" section in a document and disallow adding a second one).
Since Mobiledoc-Kit was built for a publisher originally (Bustle), the ability to intersperse text with richer content was a goal from the start. So it has a "card" concept that allows adding any rich content (embedded tweets, videos, slidehows, etc.). In fact, the Mobiledoc-Kit demo page [1] has a demo where a Codemirror editor is embedded inside the Mobiledoc editor.
It's great to see so much new energy in the browser editor world. I am hopeful that as browser features and new editors and editor features converge, we'll see some exciting new developments that broaden the perspective on what sort of content is possible to author from within a web page.
The best part of Mobiledoc is its portability; I've seen it used in both Ember.js and Riot.js, two libraries on opposite ends of the spectrum.
It's also been designed for rich, interactive content in addition to rich text. You can add interactive cards (think slideshows, before/after image sliders, etc.) and easily provide a different implementation on each platform. This lets your content authors write once and have a great experience on both native and web, and adding a new card is super easy.
One thing Draft and Mobiledoc seem to have in common is a robust programmatic API. Draft is pitched as a "framework for building rich text editors in React". Mobiledoc pitches itself as a toolkit for building WYSIWYG editors, so fairly similar goals.
Mobiledoc is designed to be very quick and easy to render, and additionally to support rich content "cards" and "atoms". These are runtime-implemented rich content sections, and our main sponsor for this work (Bustle Labs) uses them for image sliders, animated and interactive SVG features, videos, and more.
These runtime rendering points also make it easy to have multiple representations of a document. For example, we render Mobiledoc into text for ElasticSearch, into HTML for normal web visitors, and into AMP HTML. When rendering into text or AMP we can't use the normal card implementations, and runtime-configuration makes this easy.
And the rich cards work with copy paste! and undo/redo! Anyway, there is definitely overlap here, but IMO the goals of Draft are pretty narrow so far.
For a first, I'm not clear on how you would write a custom renderer against the data structure (we try to do this for Mobiledoc [0], and it has helped contributors). It seems straight ahead to derive looking at it in a debugger, but I would love to see a link to the docs on it in case I've missed them.
As for cards, we've worked hard to make sure cursors work well around cards, and around atoms. There aren't any demos of "Custom Block Components" [1] in readonly mode, but based on the inability to place a cursor after a "Code Block" with arrows when it is last, I think Mobiledoc might be a bit more robust at this date.
But conceptually custom block components are very similar to Mobiledoc cards [2]. Block based rich content with an object payload. We provide a non-block construct called "atoms" that are inline as well, for things like `@mentions` in text.
This is really neat stuff, but yup, we've been playing around with similar ideas for a few months! Happy to see so much life in the editor world again.
[0]: https://github.com/bustlelabs/mobiledoc-kit/blob/master/MOBI...
[1]: http://facebook.github.io/draft-js/docs/advanced-topics-bloc...
[2]: https://github.com/bustlelabs/mobiledoc-kit/blob/master/CARD...
[edit] looks like http://facebook.github.io/draft-js/docs/api-reference-data-c... serializes the abstract of a Draft into something persistable, and potentially renderable.
You can try the embedded photo experience in Facebook Notes at https://www.facebook.com/notes. I think it does essentially everything that you have described. (I believe we do keep at least one (possibly empty) block after each media object to allow putting the cursor there.)
Mentions (like on Facebook) can be done easily with Draft's entities, and mentions that behave more like syntax highlighting (like on Twitter) can be easily done with decorators.
Mobiledoc looks pretty awesome though. I hadn't seen it before this -- thanks for sharing.
I'm not sure how I feel about re-implementing all the things and introducing a React dependency to everything. The lock-in seems okay if React really is the final word in UI frameworks (though it is very large and currently very slow). I am certainly not in this camp, however.
[1] https://github.com/neilj/Squire
My point is, if you can have a truly reusable component with an API that can be integrated into anywhere (including a React wrapper), what do you gain by making a component that can only live in React and have no other re-usability to speak of?
There are many drawbacks, but React-native is the only benefit as far as I can see, which requires a firm React commitment.
The key insight in Draft.js is that it's incredibly difficult to keep your model in sync with the browser's contenteditable behavior unless you intercept many of the behaviors and perform them entirely in the model, then change the DOM accordingly. Medium found the same thing when building their editor:
https://medium.com/medium-eng/why-contenteditable-is-terribl...
In order to build high-quality text editors, you need to be in control of what's happening in the DOM. There's obviously many ways to control the DOM. React is a solid base for this, as are many other libraries. Facebook uses React extensively so it made sense to use as a base for Draft to avoid needing to essentially rewrite React and to make it even easier to integrate with existing React apps. Of course, you can also use Draft easily inside a non-React app.
The entire Draft model is also separate code decoupled from the UI and you're free to use the model and implement the views yourself if you're allergic to React or aren't targeting the DOM. The Facebook Groups app uses Draft's model but implements custom views for it in React Native.
Most modern rich text editors have or are moving towards having a document model that maps to the DOM, ideally also producing compact and canonical output. Quill's new document model Parchment takes this approach as well though I admit I borrowed the idea from the Etherpad guys, and some design ideas from React as well [1]. I have not had the time to dig into Draft.js in depth but Facebook has made many high quality contributions to open source and looking forward to borrowing more ideas.
Draft.JS is a controlled input. The view is completely derived from the model. This eliminates whole classes of browser and editor bugs.
It's also makes the editor really easy to extend. You can add new object types and complex styling without having to worry about all the nuances of text editing.
For example you could highlight hashtags by giving the editor a regex to match and then tell it which styles your hashtag should be decorated with. Draft.js handles creating and removing tags as you type, drag and drop, copy, paste, etc.
(disclaimer: I've been out of Facebook for a while so they may have introduced that feature already)
Undo and other text editing facilities aren't completely nonfunctional, they just become broken and unpredictable. For example, these steps:
1. Load http://facebook.github.io/draft-js/ 2. Click in the text box 3. Type 'hello' on the keyboard 4. Using the mouse, choose Edit -> Undo
The text box will show "h" instead of being empty. Do you see this too?
Note that cmd-Z won't reproduce this since the page tries to handle that itself to work around these bugs.
The Editing Task Force is working on opening necessary APIs so editor implementors could integrate with all the native features. I wrote an article about the effort [3]. It's a bit outdated now, but explains what are the goals.
[1] https://github.com/w3c/editing/issues
[2] https://github.com/w3c/editing/issues/79
[3] https://medium.com/content-uneditable/fixing-contenteditable...
Anyway this approach is the only good one if you don't want to produce crappy HTML that will end up on your site. TinyMCE and the rest are obsolete, and editors like DraftJS or mobiledoc-kit are the only viable solutions until browsers offer a new, not broken alternative to content-editable.
I was involved in a project that used contentEditable DOM as the source of truth before I had a chance to work with Draft, and I believe that Draft's model is the best compromise.
Draft does a lot to handle the edge cases where it's possible. IME and spellcheck are two of the big ones.
I'm not sure about 3rd party js text editors, but I'm guessing it could be a pain to work with state and props in that context.
Their demo falls far short of expected functionality which makes me think that there is a huge amount of work to go from this library to a working editor.
I agree that lists in web-based rich text editors, versus the de facto standard of Word, are a major problem, and I suspect they fall into that "unglamorous" area of development that people don't want to delve too deeply into.
https://github.com/facebook/draft-js/blob/f5e6cf50558f5797b0...
It would be easy to override it in your own component or send a PR upstream to make it adjust multiple blocks at once.
- When adding a long paragraph, and hitting enter in between, the caret disappears from the screen.
- When typing some text, say "abc", followed by a large number of spaces, and then selecting everything, the selection extends past the width of the editor window (colored selection bar sticks out).
- In a long paragraph, changing to a code-block, again the caret disappears.
More seriously:
- In a paragraph of 10,000 words, the editor becomes really slow, even when just typing text. This is strange because such operations (i.e., typing) are local, and React is supposed to deal only with incremental changes. I'm wondering what is going on here.
I think it's because typing a single character causes it to reparse the entire typed text in order to check if the document model needs updating.
I've seen something similar in other editors. The only solution I can naively think of is to chunk the document and only do checks within the chunk.
https://github.com/nickretallack/richer
React makes it easy to do a stateless transformation from your data model to some output, so you can model things the way you want to: as a series of overlays onto the text.
My project uses the Google Drive Realtime API as its data model, so you can collaborate on the text too. Or use the in-memory version to edit alone.
I never finished this thing. Was hoping to use it on a project at work, but got shot down. Emulating cursor movements without a text area was the fiddliest part. But I got pretty far into it as a proof of concept at least. I was impressed at how fast it was despite being a JavaScript emulation of content editable.
Building a nice declarative editing interface around an inherently mutable structure with an imperative API like the DOM (which can mutate state from under you, like focus) is a real challenge. Props to the team at facebook for this.
A few questions/concerns:
1. It doesn't look like the undo/redo state is global to the page. The built in undo/redo stack in browsers is global to the page so this is the expectation users will have. If you use multiple instances of Draft.js in your page they will each have their own stack, which is less than ideal. I realize having a singleton to manage this across all editor instances is more difficult in the React/Flux paradigm, but it would be much better from a user experience perspective. Related to this:
2. I don't think you can nest Draft.js instances. There are a number of simple cases where you might want to do this (e.g. custom block with image + caption). To support this in the future it seems you would need to account for it in the selection and undo/redo stack state handling.
3. The data format has a lot of repetition in how it stores inline styles[1]. I'm interested to know why they chose to represent the data this way instead of using indices. For instance, you can represent a inline bold style with a json object like so: {type: 'BOLD', startOffset: 5, endOffset: 10}. The format they chose to use is easier to render directly, but more difficult to parse visually and less space efficient (except in the case of styles that only cover a few characters).
Finally, it seems most modern WYSIWYG editors mentioned in the comments here were at least partially influenced by the original Medium post "Why ContentEditable is Terrible"[2]. It would be great for the community to standardize on a data model/format that could be used in all of these editors. There are enough commonalities (blocks, elements, etc.) that I believe we could create a common document spec that would be usable in all of these popular editors. The benefit of this would be having a common spec to build additional libraries for diffing, server rendering, native mobile editing, and so on.
[1] http://facebook.github.io/draft-js/docs/advanced-topics-inli... [2] https://medium.com/medium-eng/why-contenteditable-is-terribl...
Still, am I missing any kind of delta event emitter? I'd like to be able to hook edit events and push operational transform content over the wire to support collaborative editing. Other projects like ProseMirror, Quill.js and maybe Mobiledoc support this (I haven't used mobiledoc yet, but it looks like that's going to be supported Soon).
So I use CKEditor because it support everything the client want but it is like a trade with the devil. It is the worst js lib I ever had the displeasure to use, it made one developer never want to be on my project again. I needed to fix its sources, and have the non uglyfied code load in my app to debug it, because they only distribute working minimised merged js, I had to make the full sources load in webpack : pack all the js and load de dependancies in order following a config js file. The code is full of ridiculous timeout making the code unfollowable. Never use CKEditor !
I will gladly switch once it support tables.
[1]: https://medium.com/content-uneditable/ckeditor-5-the-future-...
A quick look at the code shows you use standard import/export, configurable import style for the ui part and this is great news. Good job :)
We at CKEditor are going in that exact same direction [1]. We are making a strong switch to the project, wishing to make it a powerful editing framework, just like Draft.js but in a larger scale.
[1]: https://medium.com/content-uneditable/ckeditor-5-the-future-...