How to wrap nodes of a previous selection with a new Node?

Hey there,

I’m using ProseMirror for read-only content (text, images…) that users can highlight by selecting different types of nodes (text, list, images …) and then they are saved in my database (table called Highlights) for later use

When a selection is ended (different nodes contained in the selection), I’m getting the from & to positions and save them in my database

            mouseup: (view, _) => {
              const fromPos = view.state.selection.ranges[0].$from.pos;
              const toPos = view.state.selection.ranges[0].$to.pos;
              const hasTextSelection = fromPos !== toPos;

              //check if a selection is made
              if (hasTextSelection) {
                   // ...
                   // save fromPos & toPos in my database
                   // ...

Later, I’m fetching all highlights in the database and I want to inject them back into the editor content (highlighted parts with a yellow background using a custom Node called “Highlight”)

I had 2 potential solutions in mind

1st solution (using wrap)

           // I get the "from" & "pos" from my DB

            const $from = view.state.doc.resolve(from); 
            const $to = view.state.doc.resolve(to);

            const range = new NodeRange(

            let transaction = tr.wrap(range, [
                type: state.schema.nodes.Highlight,
                attrs: { ... },


2nd solution (using slice)

           // I get the "from" & "pos" from my DB

           const slice = view.state.doc.slice(from, to);
            // create a new Highlight Node with the content of the slice
            const highlightNode = state.schema.nodes.Highlight.createChecked(

            // Replace content at the given position with the new node
            let transaction = tr.replaceRangeWith(from, to, highlightNode);


I’m not sure which one is better, but in both cases, I get the following error:

Position undefined out of range

I’m not sure to understand why positions are out of range since the content did not change between the selection saving and this operation

did I miss something?