# Disruptive content "jumps" during collaboration

**URL:** https://discuss.prosemirror.net/t/disruptive-content-jumps-during-collaboration/5703
**Category:** Uncategorized
**Created:** [July 18, 2023, 5:01pm UTC](https://discuss.prosemirror.net/t/disruptive-content-jumps-during-collaboration/5703 "2023-07-18T17:01:18Z")
**Posts on this page:** 11
**Page:** 1

<div class="post-metadata">

### Author: ![patq](https://discuss.prosemirror.net/letter_avatar_proxy/v4/letter/p/ac91a4/32.png) [@patq](https://discuss.prosemirror.net/u/patq)
#### Post date: [July 18, 2023, 5:01pm UTC](https://discuss.prosemirror.net/t/disruptive-content-jumps-during-collaboration/5703/1 "2023-07-18T17:01:18Z")

</div>

## **Problem** :

I have a collaborative editor using ProseMirror and YJS. When a peer (editor in a different tab) adds content to the top of the document, a user will experience, their content shifting lower in their viewport. I’ve noticed that this occurs even in the example demo code that is on the [YJS Docs here](https://docs.yjs.dev/ecosystem/editor-bindings/prosemirror) as well as this [Tiptap prosemirror collab scroll sandbox](https://codesandbox.io/s/ocfs29?file=/src/Tiptap.tsx) regardless of which browser I choose (firefox, chrome, safari).

> _Anyone have any insight or paths to what I should look into next?_

### screenshot:

![jumping content yjs doc example](https://discuss.prosemirror.net/uploads/secondsite/original/2X/7/70bbe19daec6965dd2a3bec775c9297e86ddc0e3.gif)

* * *

## **Research** :

- similar but different issues raised:
  - overcompensation when both scroll functionalities were implemented [issue 933](https://github.com/ProseMirror/prosemirror/issues/933) - [patch implemented](https://github.com/ProseMirror/prosemirror-view/commit/1b0d582519b8d77d1a3166320e8ed443382a110b)
    - patch already implemented, should not be causing this issue

  - [mismanaged height of containing div](https://discuss.prosemirror.net/t/undesired-scrolling-on-updatestate/2066/3)
    - my code is not adjusting the size of the containing div. visually, using browser inspector, the div looks consistent in size when content is added.

  - [misuse of resize-detector](https://github.com/ProseMirror/prosemirror/issues/933#issuecomment-498019946)
    - my code does not use a resizeObserver

- Browser has their own native scroll-anchoring mechanism. (available on all modern browsers except safari)
  - [Overview of scroll anchoring - CSS | MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/overflow-anchor/Guide_to_scroll_anchoring)
  - [W3C spec for scroll anchoring that browsers follow](https://drafts.csswg.org/css-scroll-anchoring/)

- Prosemirror has internal scrolling calculations but it does not look like we are able to hit the logic even when overflow-anchor:none

## **What I’ve tried** :

- Tried removing all custom extensions and plugins and tested the basic editor.
- A couple changes I made to allow anchoring to _WORK_ in firefox. (but not any other browser)
  - added a unique id to each paragraph/header node to help browser identify elements during rerender.
  - added `overflow-anchor: none` to all surrounding elements that are not part of the ProseMirror editor.

- Confirmed that Chrome will set its `scroll-anchor` to the ProseMirror editor and not it’s child elements
  - I believe this is due to the [w3c spec](https://drafts.csswg.org/css-scroll-anchoring/#anchor-node-selection) for choosing priority candidates during the anchor selection
    - chosen as anchor since `contenteditable = true`

---

<div class="post-metadata">

### Author: ![marijn](https://discuss.prosemirror.net/user_avatar/discuss.prosemirror.net/marijn/32/15_2.png) [@marijn](https://discuss.prosemirror.net/u/marijn)
#### Post date: [August 4, 2023, 9:13am UTC](https://discuss.prosemirror.net/t/disruptive-content-jumps-during-collaboration/5703/2 "2023-08-04T09:13:13Z")

</div>

If you can reproduce this without Tiptap and YJS, using proseMirror-collab or just plain ProseMirror primitives, I can debug it. I set up a test where I just had a timeout insert content at the top of the document, but the scroll position stayed stable there in both Chrome and Firefox.

---

<div class="post-metadata">

### Author: ![tommoor](https://discuss.prosemirror.net/user_avatar/discuss.prosemirror.net/tommoor/32/1648_2.png) [@tommoor](https://discuss.prosemirror.net/u/tommoor)
#### Post date: [February 7, 2025, 2:11am UTC](https://discuss.prosemirror.net/t/disruptive-content-jumps-during-collaboration/5703/3 "2025-02-07T02:11:05Z")

</div>

I’m unfortunately seeing the same issue, stripped everything back and the only thing that makes a difference is removing “contenteditable=true”, when you do that the scroll anchoring immediately works correctly but obviously that’s not a solution.

Really seems related to the browsers logic for choosing the correct anchor

---

<div class="post-metadata">

### Author: ![tommoor](https://discuss.prosemirror.net/user_avatar/discuss.prosemirror.net/tommoor/32/1648_2.png) [@tommoor](https://discuss.prosemirror.net/u/tommoor)
#### Post date: [February 10, 2025, 2:10pm UTC](https://discuss.prosemirror.net/t/disruptive-content-jumps-during-collaboration/5703/4 "2025-02-10T14:10:31Z")

</div>

I have filed an issue in chromium here for anyone interested you could star to get more attention on this issue that affects all instances of ProseMirror:

[https://issues.chromium.org/issues/395489580](https://issues.chromium.org/issues/395489580)

---

<div class="post-metadata">

### Author: ![tommoor](https://discuss.prosemirror.net/user_avatar/discuss.prosemirror.net/tommoor/32/1648_2.png) [@tommoor](https://discuss.prosemirror.net/u/tommoor)
#### Post date: [March 5, 2025, 1:13pm UTC](https://discuss.prosemirror.net/t/disruptive-content-jumps-during-collaboration/5703/5 "2025-03-05T13:13:52Z")

</div>

If you’re following along the chromium issue was fixed in Canary and is currently targeted to Chrome 135

---

<div class="post-metadata">

### Author: ![marijn](https://discuss.prosemirror.net/user_avatar/discuss.prosemirror.net/marijn/32/15_2.png) [@marijn](https://discuss.prosemirror.net/u/marijn)
#### Post date: [March 5, 2025, 1:53pm UTC](https://discuss.prosemirror.net/t/disruptive-content-jumps-during-collaboration/5703/6 "2025-03-05T13:53:12Z")

</div>

That’s great. Thanks for filing that bug.

---

<div class="post-metadata">

### Author: ![SMores](https://discuss.prosemirror.net/user_avatar/discuss.prosemirror.net/smores/32/3356_2.png) [@SMores](https://discuss.prosemirror.net/u/SMores)
#### Post date: [September 8, 2025, 8:25pm UTC](https://discuss.prosemirror.net/t/disruptive-content-jumps-during-collaboration/5703/7 "2025-09-08T20:25:02Z")

</div>

Oof, it seems like this is still an issue in both Firefox and Safari. I guess I can try to open similar issues against those projects. Are folks doing anything as workarounds in the mean time? This is a pretty rough experience.

For reproduction, you don’t even need prosemirror-collab. Here’s a plugin that will trigger it:

```ts
  new Plugin({
    view(view) {
      let intervalId = null;
      const button = document.createElement("button");
      button.type = "button";
      button.appendChild(document.createTextNode("Start typing"));
      button.addEventListener("click", () => {
        if (intervalId) {
          clearInterval(intervalId);
          button.innerText = "Start typing";
        } else {
          intervalId = setInterval(() => {
            view.dispatch(view.state.tr.insertText("a", 1));
          });
          button.innerText = "Stop typing";
        }
      });
      view.dom.parentElement.prepend(button);

      return {
        destroy() {
          if (intervalId) {
            clearInterval(intervalId);
          }
          button.remove();
        },
      };
    },
  }),

```

You have to scroll down in the document, and then you have to focus the document. Once your cursor is in the document below where text is being auto-inserted, your cursor will get pushed down one line every time the plugin types one line worth of content. Basically, it seems that _when there is an active selection_, the scroll becomes pinned to the top of the document, rather than to the viewport (or something, I didn’t describe that well).

---

<div class="post-metadata">

### Author: ![marijn](https://discuss.prosemirror.net/user_avatar/discuss.prosemirror.net/marijn/32/15_2.png) [@marijn](https://discuss.prosemirror.net/u/marijn)
#### Post date: [September 9, 2025, 10:15am UTC](https://discuss.prosemirror.net/t/disruptive-content-jumps-during-collaboration/5703/8 "2025-09-09T10:15:41Z")

</div>

With that plugin, in both Firefox and Chrome Linux, my visible content seems to remain entirely stable when I have the cursor at the bottom of the page with the insertion position scrolled out of view.

Could you say a bit more about what browser you’re testing with? And how your editor handles scrolling general (full page, editor element, some wrapper)?

---

<div class="post-metadata">

### Author: ![SMores](https://discuss.prosemirror.net/user_avatar/discuss.prosemirror.net/smores/32/3356_2.png) [@SMores](https://discuss.prosemirror.net/u/SMores)
#### Post date: [September 10, 2025, 5:15pm UTC](https://discuss.prosemirror.net/t/disruptive-content-jumps-during-collaboration/5703/9 "2025-09-10T17:15:58Z")

</div>

I’m using Firefox on Linux. I made a little demo editor, here’s the full HTML and CSS (I’m mounting the EditorView on the `root` element):

```html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>ProseMirror Demo</title>
  </head>
  <body>
    <div id="root"></div>
    <script type="module" src="/demo/main.tsx"></script>
  </body>
</html>

```

```css
.ProseMirror {
  border: thin solid black;
  border-radius: 0.25rem;
  padding: 1rem;
  outline: none;
  min-height: 200px;
}

#root {
  margin: auto;
  width: 80%;
  max-width: 700px;
}

```

So, it’s the body that’s actually scrolling as the editor grows.

Here’s a little video of the issue in case that’s useful: [Screencast From 2025-09-10 13-07-57.mp4 - Nextcloud](https://nextcloud.friedmans.us/index.php/s/SkeQEEHXdn64CBt) (I know the tab says “React ProseMirror Demo”, but that’s just because I reused that demo setup — this is with plain prosemirror-view, no React)

Originally we found this in a much more complex editor, using WebKit, on both Linux and macOS (via Tauri, a desktop app framework). I’ll take a look at that and see what element is scrolled there, too

---

<div class="post-metadata">

### Author: ![SMores](https://discuss.prosemirror.net/user_avatar/discuss.prosemirror.net/smores/32/3356_2.png) [@SMores](https://discuss.prosemirror.net/u/SMores)
#### Post date: [September 10, 2025, 5:28pm UTC](https://discuss.prosemirror.net/t/disruptive-content-jumps-during-collaboration/5703/10 "2025-09-10T17:28:49Z")

</div>

In case it’s relevant, I am seeing the same behavior if I fix the height of the EditorView’s dom, such that only its content scrolls, e.g.

```css
.ProseMirror {
  border: thin solid black;
  border-radius: 0.25rem;
  padding: 1rem;
  outline: none;
  min-height: 200px;
  overflow-y: auto;
  height: 80dvh;
}

#root {
  margin: auto;
  width: 80%;
  max-width: 700px;
}

```

---

<div class="post-metadata">

### Author: ![marijn](https://discuss.prosemirror.net/user_avatar/discuss.prosemirror.net/marijn/32/15_2.png) [@marijn](https://discuss.prosemirror.net/u/marijn)
#### Post date: [September 11, 2025, 7:39am UTC](https://discuss.prosemirror.net/t/disruptive-content-jumps-during-collaboration/5703/11 "2025-09-11T07:39:10Z")

</div>

Okay, I can reproduce this now. It seems to be an issue in Firefox’s native scroll anchoring. It has been present at least since version 121 so not a recent regression I guess. You can reproduce it with this simple page as well (note view is stable until you focus the editable content):

```auto
<div contenteditable=true></div>

<script>
  let d = document.querySelector("div")
  for (let i = 1; i < 100; i++) {
    d.appendChild(document.createElement("p")).textContent = "Paragraph " + i
  }
  window.scrollTo(0, 1e6)
  setInterval(() => {
    d.insertBefore(document.createElement("p"), d.firstChild).textContent = "New paragraph"
  }, 500)
</script>

```

We could do our own custom scroll anchoring, I guess, but that’s quite a messy and error-prone thing to implement, and super disruptive when it has bugs.

I’ve filed a [Firefox issue](https://bugzilla.mozilla.org/show_bug.cgi?id=1988109) for the time being.
