Async toDOM

Hi there!

New to ProseMirror and it seems to be really cool!

I just tried to develop a Post Link (Mark). The reason is that on rendering I always need the latest slug of the post to create the url with. I thought to do an async request to an api enpoint to fetch the post and create the href attribute of the link with these data. It seems an async function is not allowed for the toDOM function.

toDOM: async node => ['a', {
            href: await this.getPostLink(,
        }, 0],

Am I going the wrong route, am I missing something or is there a different solution?




I am assuming this would make the editor quite slow to render, especially if it is re-rendered because something is changed. There are two things that come to mind:

  1. Save the URL in an attribute, when you load the document (or on some other action) run transactions on the document replacing all post URLs with current ones (you’d be replacing the attribute).

  2. Create a fixed route a la yourserver/post/ID123 and then let the server resolve to the current URL when the link is followed

Question to ask here would be: When/Why does the URL change.

Best regards Frederik

This sounds nice. How would that work? I’ve been diving into the docs, but the learning curve is pretty steep. If you could guide me in the right direction I’m will probably find it out.

I just did an easy fix for now: After the dom is loaded I just search for a[data-post], fetch it’s slugs from the server and replace the urls. By the way: I’m only doing this on the website side, so not in the editor itself, because it’s nod required there. If there is a nicer way with ProseMirror I’m happy to hear so.

That sounds very reasonable if you need the information at the time of publishing. ProseMirror has no prescriptive way in that respect. The only thing it asks is, that you use transformations within the editor instead of just replacing nodes in its document tree - of course when you do the replacement just for rendering that should be fine.

At my job, we actually only render the JSON, not the ProseMirror state itself (and we do it server side so it can be cached). If you have any more doubts, feel free to post your solution.

Best Frederik

Hi @frederik !

Thanks for your reply! That’s how I did it before as well. I built a block editor which saves to json on the server. On the server the content got parsed for short codes, i.e. here).id(95), and the parsed content then is cached. It works fine, but the short codes aren’t very user friendly, so I went this route. Also hoping I can place the short code in the content with a Mark for example and then just have it displayed differently in the editor. That way I can still parse and cache as before. Do you see this being possible (and not being too hard) with ProseMirror?


I am not sure I understand the scenario. Who enters the short codes where?

ProseMirror is wonderful at editing text and even keeping attributes and marks up to date. What it doesn’t do well is keeping all the external metadata up to date because each modification to the document requires a transaction (e.g. I would save comment ids, links src etc directly in the document but would save comment texts and other information in a separate place).

I would ask myself: Is this something that is changed in the editor? Then definitely keep that in marks or node attributes and update the document through transactions.

Is this something that is changed in an outside system? Then probably it is best to just store an ID in ProseMirror where you want the link to go and then replace the metadata post editing.

Maybe someone else gets it better than me, or you may want to post a screenshot or something before I can recommend a pattern.

It sounds like you’re describing a custom mark (perhaps PostLink) with an attr like a post_id. Then in the json you’d see something like:

      "content": [
          "text": "Some text without any marks",
          "type": "text"
          "marks": [
              "attrs": {
                "post_id": 1234
              "type": "post_link"
          "text": "linked text",
          "type": "text"
          "text": " some more unmarked text ",
          "type": "text"
      "type": "paragraph"

It’s a bit of work to get your head around, but once you do there is a great deal of flexibility and power in custom nodes and marks.

Hey @frederik & @astevenson Thank you both for your replies. @astevenson that’s actually almost exactly what I’ve got already. I also have a default href now, which I later replace for the current link. Although I’m no also curious if it’s fairly easy to have this workflow:

  1. Click post link button @ editor
  2. Open form where one can choose the desired post (got this working)
  3. A short code (i.e. [post id=“1”]link[/post] or link or is then implemented (don’t know how this would result within the data structure of ProseMirror)
  4. In the editor the user sees a link instead of the short code
  5. On the server side the short code will be parsed and cached as html (got this working)
  6. On the website the parsed html will display a link

Hope this is clear enough else I’ll compliment it with screenshots

I don’t really get the shortcode bit. Are you saying you’re using this with WordPress? If your final product is html rendered from ProseMirror generated JSON, your mark already has all the data you need to replace it with a link. If you’re stopping at something like WP post text that needs shortcodes then you’ll need some sort of custom serializer that generates a shortcode from your mark.

This is how we render our content using React: We have another library that’s written ruby that isn’t currently public that behaves in a similar manner. It gives us a great deal of control in terms of how we render our output and means all of our Prosemirror custom schema and NodeViews are only concerned with the editor-side view.

It’s a custom written control panel, but yes it works a bit similar with the short codes to WP. Since the control panel supports short codes I went that route, but since I have it stored as a[data-post] I can parse that as well. Thanks for reminding me of that!