16 votes

Making React ProseMirror really, really fast

26 comments

  1. [6]
    smores
    Link
    I don't know if folks are interested in this kind of niche technical deep dive here, no worries if not. But I figured I would share in case anyone is, like, really into React development, or...

    I don't know if folks are interested in this kind of niche technical deep dive here, no worries if not. But I figured I would share in case anyone is, like, really into React development, or something!

    4 votes
    1. [4]
      regularmother
      Link Parent
      This is awesome. I just started a collaborative editing project and really enjoyed this.

      This is awesome. I just started a collaborative editing project and really enjoyed this.

      2 votes
      1. [3]
        smores
        Link Parent
        Thanks! Glad to hear it. What kind of collaborative editing project are you working on?

        Thanks! Glad to hear it. What kind of collaborative editing project are you working on?

        1 vote
        1. [2]
          regularmother
          Link Parent
          It's https://loreweaver.no, but I'm still getting it functional before I fully open source it. Soon (weeks?). I also need to pick a different name because I'm the fifth loreweaver, unfortunately.

          It's https://loreweaver.no, but I'm still getting it functional before I fully open source it. Soon (weeks?).

          I also need to pick a different name because I'm the fifth loreweaver, unfortunately.

          1 vote
          1. smores
            Link Parent
            Oh very cool! Shoot me a link when you publish it, I'd love to take a look!

            Oh very cool! Shoot me a link when you publish it, I'd love to take a look!

            2 votes
    2. Gazook89
      Link Parent
      I’m very into Codemirror, but have considered moving towards ProseMirror. Haven’t read the article yet though.

      I’m very into Codemirror, but have considered moving towards ProseMirror. Haven’t read the article yet though.

      1 vote
  2. [6]
    xk3
    Link
    If anything the second one seemed more laggy than the first. idk... maybe it is different on Apple Silicon... My browser shuddered and froze for a moment there--YOU'RE PLAYING WITH PEOPLE'S TABS!...

    But, hear me out, it’s really fast:

    If anything the second one seemed more laggy than the first. idk... maybe it is different on Apple Silicon...

    My browser shuddered and froze for a moment there--YOU'RE PLAYING WITH PEOPLE'S LIVES TABS! The tabs are on the line. The tabs are online...

    Perhaps we could stop putting things in the Shadow DOM??! just an idea!

    HTML is a pretty good format for documents. The browser is already pretty complicated even just with HTML5 and CSS3--we don't need to build another browser inside the browser!

    2 votes
    1. [5]
      smores
      (edited )
      Link Parent
      Oh, huh. I guess I should test this on Safari — on both Chrome and Firefox (including on my Android) there is no lag at all for me on the second editor. There's no Shadow DOM on this page, so I'm...

      Oh, huh. I guess I should test this on Safari — on both Chrome and Firefox (including on my Android) there is no lag at all for me on the second editor.

      There's no Shadow DOM on this page, so I'm not exactly sure what you're referring to there!

      Just out of curiosity, do you also see slowness editing the document on https://handlewithcarecollective.github.io/react-prosemirror ? Maybe there's just an issue with my demo editor on the blog post.

      It's basically not possible for the second editor to be slower — it's doing like 1/100th the work. It is possible that your browser is struggling with rendering the entire contents of Moby Dick twice, though, which I didn't really think about. Maybe when you blur the editors I should actually truncate the document, in addition to making it static

      3 votes
      1. [2]
        xk3
        (edited )
        Link Parent
        That page still seems a bit laggy and it interferes with native keyboard editing on mobile (like character, word selection and predictive text) I only tried it on Firefox and Fennec tbh I have...

        That page still seems a bit laggy and it interferes with native keyboard editing on mobile (like character, word selection and predictive text)

        I only tried it on Firefox and Fennec

        tbh I have little patience for input lag and weird behaviors compared to native textboxes (like the ones Tildes uses). I've never had a good experience with something that tries to.... I don't even know what you're trying to do?? Like what is the benefit of this type of interface that takes control over the normal web components via JavaScript?

        https://handlewithcare.dev/blog/why_i_rebuilt_prosemirror_view/

        okay I get it a bit more... it's a rich text editor like the WordPress live preview mode thing

        2 votes
        1. smores
          (edited )
          Link Parent
          Sorry, I was mixing up responses. Just making sure I understand, you're using Firefox on an Apple laptop/desktop? Or on iOS? Right, there's no point in using this for a plain textarea. The library...

          My guess is that this is an Android keyboard thing — Gboard, in my experience, works fine with React ProseMirror, but Swype and Samsung keyboard have issues. Maybe this will be my push to try to actually resolve these, I think it's gonna be a pain haha. Sorry, I was mixing up responses. Just making sure I understand, you're using Firefox on an Apple laptop/desktop? Or on iOS?

          Right, there's no point in using this for a plain textarea. The library is designed (and used) for complex rich text editors, like dskrpt.de, which uses it for their textbook editor (with inline quizzes and other interactive elements) and Moment which uses it for its rich markdown editor, including rather elaborate interactive code blocks. Web browsers don't have any native components for this (ultimately we're using contenteditable, but contenteditable on its own is not usable as a rich text editor).

          Even for just a simpler text editor, though, it's not necessarily as simple as "just use native web components." I definitely underestimated both the number of mobile Android users reading this and the variety of mobile Android experiences of React ProseMirror (always a mistake, web on Android is nothing if not chaos), but React ProseMirror is much faster than the native contenteditable element on desktop Firefox.

          Anyway, sorry the demos frustrated you, that obviously wasn't the goal (well, it was a little bit the goal for the first editor)! I

          2 votes
      2. [2]
        polle
        Link Parent
        I read this comment first before reading your blog. There is definitely still a rendering issue somewhere. I loaded the second editor before the first one (to avoid the scenario you mention about...

        I read this comment first before reading your blog. There is definitely still a rendering issue somewhere. I loaded the second editor before the first one (to avoid the scenario you mention about having multiple moby dicks).

        Simply pressing enter on each editor (old and new) has very noticible input lag (feels like close to half a second before the enter is inserted). Doing this on android (firefox).

        I don't use either library and have no use for this, but given the effort you went through, I assume you would want to know and have another look.

        Edit: Even stranger. Typing any letter causes that letter "popup" (default android keyboard) to remain in its popup state. So I press 'g', which on an android keyboard causes 'g' to briefly flash as a popup letter in the keyboard, but on your editor, the key stays forever. Very strange stuff. Have not seen this anywhere else before

        1 vote
        1. smores
          Link Parent
          I think this is ultimately due to some Android devices just having trouble with rendering this many DOM elements in a contenteditable. Weirdly not an issue with Firefox on my Fairphone 5 at all —...

          I think this is ultimately due to some Android devices just having trouble with rendering this many DOM elements in a contenteditable. Weirdly not an issue with Firefox on my Fairphone 5 at all — even the first editor is surprisingly fast for me haha.

          I make this mistake a lot, unfortunately, of testing while I'm writing on my laptop and forgetting that phone have completely different performance characteristics.

          1 vote
  3. [2]
    Exellin
    Link
    In both the before and after text editor, my browser immediately crashed when clicking on it. Using edge on android.

    In both the before and after text editor, my browser immediately crashed when clicking on it. Using edge on android.

    2 votes
    1. smores
      Link Parent
      I think I should have anticipated that some browsers and some mobile devices wouldn't be able to handle a contenteditable with an entire novel in it. Sorry I crashed your browser :/ I'll truncate...

      I think I should have anticipated that some browsers and some mobile devices wouldn't be able to handle a contenteditable with an entire novel in it. Sorry I crashed your browser :/ I'll truncate the demo text and see if the point still gets across.

      2 votes
  4. [3]
    post_below
    Link
    Works well for me on android Chrome. Nice job optimizing. I'm going to be the one to say it: The best way to make it faster is not to use react! :)

    Works well for me on android Chrome. Nice job optimizing.

    I'm going to be the one to say it: The best way to make it faster is not to use react! :)

    2 votes
    1. [2]
      smores
      (edited )
      Link Parent
      I'm not sure if you saw the last section, but on (desktop) Firefox this is actually not true! React ProseMirror is much faster than Firefox's native contenteditable implementation, even without a...

      I'm not sure if you saw the last section, but on (desktop) Firefox this is actually not true! React ProseMirror is much faster than Firefox's native contenteditable implementation, even without a ProseMirror editor set up at all

      2 votes
      1. post_below
        Link Parent
        That's interesting, a native implementation has to do less work, with less overhead, and with compiled code. Implies that Firefox got something pretty badly wrong.

        That's interesting, a native implementation has to do less work, with less overhead, and with compiled code. Implies that Firefox got something pretty badly wrong.

        2 votes
  5. [3]
    smores
    Link
    Ok, thanks everyone for the feedback! I have: Added some notes/caveats about issues that may crop up with the demo editors Configured the demo editors to truncate the document when unfocused, so...

    Ok, thanks everyone for the feedback! I have:

    1. Added some notes/caveats about issues that may crop up with the demo editors
    2. Configured the demo editors to truncate the document when unfocused, so that your browser isn’t rendering Moby Dick twice.

    Also discovered some interesting things:

    • I didn't really account for how much faster React's production build is than the development build. I basically can't type fast enough on my phone (Android or iPhone) to even notice the lag on the unmemoized editor
    • Firefox on macOS is just crazy slow for large documents, basically no matter what. Just putting Moby Dick in a plain contenteditable is slow, putting it in the unmemoized editor is slow, and putting it in the memoized editor is best but still pretty slow
    • Safari on macOS is crazy fast haha. I couldn't discern any lag at all on the unmemoized editor when I was testing on my m4 Mac Mini
    2 votes
    1. [2]
      gary
      Link Parent
      Amazing write-up. I'm excited to go back and read it in depth. I noticed on Mac (M2 Max) Safari, they feel equally fast yet slightly stuttery to me. Like very slightly stuttery to the point most...

      Amazing write-up. I'm excited to go back and read it in depth. I noticed on Mac (M2 Max) Safari, they feel equally fast yet slightly stuttery to me. Like very slightly stuttery to the point most wouldn't perceive it. I suspect an intentional limitation for battery reasons but that the machine is so fast it doesn't seem slow overall. Mac Chrome, both feel great and better than Safari. iOS Safari I instantly feel that the memoized editor is far better.

      An interesting test might be power consumption if it were feasible to measure. Anyway, fantastic post again.

      2 votes
      1. smores
        Link Parent
        Thank you! And thanks for testing it out a bit, good to have more data points. It's super interesting how variable this can be across different operating systems and browsers.

        Thank you! And thanks for testing it out a bit, good to have more data points. It's super interesting how variable this can be across different operating systems and browsers.

        1 vote
  6. [2]
    Gazook89
    Link
    Offtopic: On my iphone, with Orion browser, I had no problem with this website. Just moved to mac with Orion browser again, and it took a long time to load the page. And, I can't seem to load the...

    Offtopic: On my iphone, with Orion browser, I had no problem with this website. Just moved to mac with Orion browser again, and it took a long time to load the page. And, I can't seem to load the first editor by clicking on it? And, just before that, the "@nytimes/react-prosemirror@0.7.0-next.10" part appears as "@nytimes/[email protected]" and when clicked takes me to a cloudflare error that tells me it is protecting an email address?

    1 vote
    1. smores
      (edited )
      Link Parent
      That's.. all incredibly odd haha. The website is fronted by Cloudflare, but I am not aware of any email protection settings (maybe it's on by default?), and I'm not personally seeing that even if...

      That's.. all incredibly odd haha. The website is fronted by Cloudflare, but I am not aware of any email protection settings (maybe it's on by default?), and I'm not personally seeing that even if I go off of WiFi. Let me poke around and see what I can find...

      Update:

      Seems like this is indeed enabled by default, and I should be able to turn it off for that string, as well as in general (I already have my own email obfuscation implemented on that site, so I don't have any need for it).

      However, the fact that you're seeing that at all is an indication that Cloudflare thinks you might be a bot, which I'm sure is exciting news. That might explain why the site loaded so much more slowly for you on your desktop, as well.

      2 votes
  7. [2]
    creesch
    (edited )
    Link
    Something is not right when I try it with chrome on Android. Specifically hitting enter caused weird behavior in all examples. In the first one it doesn't work, at the end of a word it does...

    Something is not right when I try it with chrome on Android. Specifically hitting enter caused weird behavior in all examples. In the first one it doesn't work, at the end of a word it does duplicate the word.

    The last fast example somewhat works but it duplicates one character for each line break.

    Edit:

    I did some more testing and it seems to be keyboard related. Gboard works okay, Samsung keyboard has its own weird behavior and the keyboard I initially tested with is heliboard.

    I am suspect that both the Samsung keyboard and heliboard might do something slightly odd. But now you at least know they cause issues. I honestly have checked with native posemirror, let me do that as well and get back at you.

    Edit2:

    Prosemirror on their website works fien with heliboard.

    Edit3:

    Seems like Android (as often is the case) is a bit of a mess. I found some interesting stuff as I was curious.

    It looks like prosemirror has a variety of workarounds implemented (this is quite old but seems to be about pretty much this. If I had to guess is that some of those work arounds aren't present in some of the bits you rewrote in react.

    1 vote
    1. smores
      Link Parent
      We actually need completely different workarounds for Android keyboards, because we use beforeinput events (native ProseMirror does not) and several Android keyboards produce completely broken...

      We actually need completely different workarounds for Android keyboards, because we use beforeinput events (native ProseMirror does not) and several Android keyboards produce completely broken beforeinput event sequences on Android Chrome. Slate.js has worled out resolutions to this that we need to take a look at.

      1 vote
  8. [2]
    itsthejoker
    Link
    On stock latest android and latest firefox, replacements using the Gboard buttons (like "we're" -> "were") take upwards of a full second to insert into the text area on the new version. Typing...

    On stock latest android and latest firefox, replacements using the Gboard buttons (like "we're" -> "were") take upwards of a full second to insert into the text area on the new version. Typing seems to be pretty fast, though.

    1 vote
    1. smores
      Link Parent
      Oh! I can reproduce this, too. I wonder what the heck Gboard is doing here to cause this. I'll have to take a look!

      Oh! I can reproduce this, too. I wonder what the heck Gboard is doing here to cause this. I'll have to take a look!

      1 vote