How I can attach attribute with dynamic value when new paragraph is inserted?

Hi guys,

I would like to attach custom attribute to each paragraph node that prosemirror insert during user interaction. Custom attribute should be with dynamic value something like GUID. Basically I would like to register listener which will "listen" for inserting/deleting of new paragraph and when this happens will ask backend service for GUID value and attach it to paragraph attribute. Can you advise me how I can achieve this?

Thank you in advance!

The recommended way to do this is to add an attribute with a default value of null to your paragraph node type, and register a plugin with an appendTransaction function that scans the document and assigns a new ID to each document with a duplicate or missing ID.

1 Like

Hi there,

Just faced with the same task and want to share my solution to save time of the next generation of ProseMirror lovers :slight_smile:

Plugin file:

import {Plugin} from "prosemirror-state";
import {uuidv4} from "uuidv4";

const isTargetNodeOfType = (node, type) => (node.type === type);

const isNodeHasAttribute = (node, attrName) => Boolean(node.attrs && node.attrs[attrName]);

const attrName = "guid";

export const createPlugin = (guidGenerator = uuidv4) => {
  return new Plugin({
    appendTransaction: (transactions, prevState, nextState) => {
      const tr =;
      let modified = false;
      if (transactions.some((transaction) => transaction.docChanged)) {
        // Adds a unique id to a node
        nextState.doc.descendants((node, pos) => {
          const {paragraph} = nextState.schema.nodes;
          if (isTargetNodeOfType(node, paragraph) && !isNodeHasAttribute(node, attrName)) {
            const attrs = node.attrs;
            tr.setNodeMarkup(pos, undefined, {...attrs, [attrName]: guidGenerator()});
            modified = true;

      return modified ? tr : null;

Schema file:

import {Schema} from "prosemirror-model";
import {marks, nodes} from "prosemirror-schema-basic";

const baseNodes = {
  // customize basic paragraph with guid
  paragraph: {
    content: "inline*",
    group: "block",
    attrs: {
      guid: {default: ""},
    parseDOM: [
        tag: "p",
        getAttrs: (dom) => ({guid: dom.getAttribute("data-guid")}),
    toDOM(node) {
      const {guid} = node.attrs;
      return ["p", {"data-guid": guid}, 0];

const base = new Schema({nodes: baseNodes, marks: marks});

Thank you @marijn for such a wonderful library!

Good luck!