# Modify specific node on copy and paste (in clipboard)

**URL:** https://discuss.prosemirror.net/t/modify-specific-node-on-copy-and-paste-in-clipboard/4901
**Category:** Uncategorized
**Created:** [September 9, 2022, 11:45am UTC](https://discuss.prosemirror.net/t/modify-specific-node-on-copy-and-paste-in-clipboard/4901 "2022-09-09T11:45:23Z")
**Posts on this page:** 1
**Showing post:** 4

<div class="post-metadata">

### Author: ![Inmate](https://discuss.prosemirror.net/user_avatar/discuss.prosemirror.net/inmate/32/458_2.png) [@Inmate](https://discuss.prosemirror.net/u/Inmate)
#### Post date: [September 26, 2022, 11:32am UTC](https://discuss.prosemirror.net/t/modify-specific-node-on-copy-and-paste-in-clipboard/4901/4 "2022-09-26T11:32:27Z")

</div>

Thought I will share some details of how I made it eventually. The general concept is that on copy we update attribute with external data and remove it on paste when it’s not needed. This approach may be helpful to support some similar workflows.

First, I added new attrs to my custom Node called `copyPasteData` which expects a stringified object with external data structure.

```auto
...
toDOM: (node: ProsemirrorNode): DOMOutputSpecArray => {
    return [
      "span",
      {
        ...,
        "data-copy-paste-data": JSON.stringify(node.attrs.copyPasteData),
      },
    ];
  },

  parseDOM: [
    {
      tag: "span[data-custom-field]",
      getAttrs: (dom: string | Node) => {
        const { ..., copyPasteData } = (dom as HTMLElement).dataset;

        return {
          ...,
          copyPasteData: copyPasteData && JSON.parse(copyPasteData),
        };
      },
    },

```

Then, I created reusable `mapSlice` and `mapFragement` functions that will traverse the Slice and with callback function update Nodes we need (remember to not mutate the Fragment directly)

```auto
import { Slice, Fragment, Node } from "prosemirror-model";

export const mapFragment = (
  fragment: Fragment,
  callback: (node: Node) => Node | Node[] | Fragment | null
): Fragment =>
  Fragment.fromArray(
    (fragment as any).content.map((node: Node) => {
      if (node.content.childCount > 0) {
        return node.type.create(
          node.attrs,
          mapFragment(node.content, callback)
        );
      }

      return callback(node);
    })
  );

export const mapSlice = (
  slice: Slice,
  callback: (node: Node) => Node | Node[] | Fragment | null
): Slice => {
  const fragment = mapFragment(slice.content, callback);
  return new Slice(fragment, slice.openStart, slice.openEnd);
};

```

Then I have a single plugin with `transformCopied` and `transformPasted` that accepts external context

```auto
export const copyPastePlugin = ({ editorContext }: Args) =>
  new Plugin({
    key: new PluginKey("copyPastePlugin"),
    props: {
      transformCopied: (slice: Slice) => {
        /* Add temporary dataForCopyPaste to live in the clipboard. */
        const addCopyPasteDataToDataFields = (node: Node) => {
          if (node.type === schema.nodes.custom_field) {
            // do something with external context

            return node.type.create(
              {
                ...node.attrs,
                ...(someData && {
                  copyPasteData: { ...someData },
                }),
              },
              node.content
            );
          }

          return node.copy(node.content);
        };

        return mapSlice(slice, addCopyPasteDataToDataFields);
      },
      transformPasted: (slice: Slice) => {
        // get dataForCopyPaste and do whatever with data + clean it up
        const fetchAndClearCopyPasteData = (node: Node) => {
          if (
            node.type === schema.nodes.custom_field &&
            node.attrs.copyPasteData
          ) {
            // do something with copyPasteData and clean it up
            return node.type.create(
              { ...node.attrs, copyPasteData: undefined },
              node.content
            );
          }
          return node.copy(node.content);
        };

        const newSlice = mapSlice(slice, fetchAndClearCopyPasteData);

        return newSlice;
      },
    },
  });

```

Also, a side note, if your external context can change during Editor updates you’ll need to call `state.reconfigure()` on each change to initialize new set of plugins with fresh context (or at least that’s the only working solution I found).

In my case, I crated a helper function which updates plugins whenever I need to:

```auto
const newState = editorView.state.reconfigure({
    plugins: [...otherPlugins, copyPastePlugin(editorContext)],
  });

  editorView.updateState(newState);

```

---

_[View the full topic](https://discuss.prosemirror.net/t/modify-specific-node-on-copy-and-paste-in-clipboard/4901)._
