Does `tr.setNodeAttribute` reset stored marks?

I’m experiencing a wired behavior from setNodeAttribute. It resets the stored marks. Here is a simplified version of my plugin:

function TextDirectionPlugin() {
  return new Plugin({
    key: new PluginKey("textDirection"),
    appendTransaction: (transactions, oldState, newState) => {
      let modified = false;
      const tr =;

      newState.doc.descendants((node, pos) => {
        if ( === "paragraph") {
          // Resets marks:
          tr.setNodeAttribute(pos, "dir", getTextDirection(node.textContent));
          // tr.storedMakrs === null
          modified = true;

      return modified ? tr : null;

After tr.setNodeAttribute() runs, tr.storedMarks becomes null.

I’m using this plugin in TipTap, and I’m skeptical this is a TipTap bug. But, I wanted to make sure this is NOT the expected behavior.

Yes, adding any step to a transaction clears the stored marks it seems. I’m not sure that’s a good idea, fundamentally, but since that’s how it’s worked since the initial 1.0 release, and it is appropriate in a lot of situations (when you type, or otherwise insert content at the selection, that should clears the stored marks for sure), I don’t think that’s something we can change without causing issues at this point.

1 Like