Custom mark with WebComponents

Hello ProseMirror community,

I’m new to ProseMirror and need some assistance with marks in the editor.

My goal is to wrap text, or text selection with a web component’s custom element. I achieved this with a custom mark, and it worked as expected until I tried to define two marks in the document.

The creation of the more than 1 mark works fine and multiple custom marks can be created.

It is when the ProseMirror editor gets created and parse’s the DOM with more than one custom mark, the web component will be created indefinitely until the browser runs out of memory.

I have tested the creation of my web component in an isolated environment and did not experience this bug, it appears to be related to when the custom element gets “rendered” in prose mirror.

I could provide more information and code if needed.

Does the component use a shadow root? If so, yes, that’d probably break—the editor expects to be able to see and control the content of the node, which breaks with a shadow root.

1 Like

Yep, the component uses a shadowroot. I’ll try to work around this. Thanks for the quick reply.

May you go into further detail as to why it would break?

@marijn We have been able to create custom nodes using web components without problems. Are you saying that marks work differently and that the shadowRoot of a mark in particular would cause problems?

As mentioned in the OP, we get this error only when there is more than one mark of this type in our doc. If there is only one element, it works just fine. It’s when we add a second one and modify the node’s style, for example, that ProseMirror gets stuck in an infinite loop.

By contrast, when testing with Nodes that use web components, we are able to add any number of web components to the doc, within various nodes, without any problems (all of which have a shadowRoot).

We may end up having to use a Node for this, but we’d like to better understand what the problem here is so that we can address it and enforce a contract of sorts.

If these are nodes with content, I don’t expect them to behave much differently from marks. I can’t tell you exactly what’s happening in those infinite loops you get—I haven’t tried debugging this.

I’ve gone ahead and created a simple sample application to help debug this and would like to share it with you. The sample application is modeled after our principal application and is missing a menu bar from our modeled application, but the goal was to demonstrate that the custom mark within the editors DOM would cause the issue and was inserted directly into the html. You can find it here, any help is appreciated.

It’s worth noting that I have experimented with the marks definition to ensure that the attributes are being set correctly to the mark. I had received some questionable results testing this and would receive different values in the toDOM based if the attribute is defined in the html first, the webcomponent or defined by prosemirror in the marks attrs property.

The issue appears to mainly manifest when the prosemirror editor changes the property of the custom mark. I’d like to have a better understanding of what and how prosemirror is behaving with the marks attributes. It looks like the issue may be a race condition between the reflected property value from the web component and the prosemirror state, where the application ends up in a loop between two state managers trying to determine the attribute value of the mark/webcomponent.

I’m not going to debug that. It’s still a lot of code, and what you’re describing is simply not supported. When you attach a shadow root to an element, our code can’t use normal DOM child manipulation to adjust its content, so even if there weren’t an infinite loop, I don’t see how that could allow the prosemirror view to maintain the node’s content.

Hey @marijn,

The way our web components work is that they expose a slot within the element. Content is placed into the slot that is located in the shadowRoot, but the slot’s contents are accessible in the LightDOM. We don’t hide any content that is being manipulated by the editor into the shadowRoot, so there’s no reason it should have difficulty accessing the content. It’s accessed in the same way you would access any other element’s children. In fact, we have sometimes used the shadowRoot in order to hide content from ProseMirror. We’ve also done many tests creating nodes with various web components and we have had no issues using them (but we hadn’t tested marks yet, unfortunately).

We are quite confident that the problem does not have to do with the shadowRoot as the problem persists even after we remove the shadowRoot altogether.

That all being said, it seems the problem may rest somewhere else within our application, outside the scope of ProseMirror and LitElement.

Thank you very much for your time!


Hey @marijn ! Sorry to revive this issue.

I am currently having this problem. I originally had the web component (a tooltip component) with shadowRoot, but as it was only 1 instance, I didn’t have the loop issue. I did have issues with text selection, so I removed shadowRoot and everything worked fine.

I now have a use case where I need to add multiple tooltips, and even though the shadowRoot is set to false, whenever I add the marks, I run into an inifinite render loop.

Any ideas on how to solve this?

As I was looking into ProseMirror and WebComponents, I stumbled upon this question. It looks like the behavior that you are describing is no longer happening - at least I cannot reproduce it from cloning your github repo with the reproducer. Is that correct, and if so - what changed?