Is pickable: false the correct approach for locking nodes/links during real-time collaboration?

We are implementing real-time collaboration in our diagramming application. As part of this, When User A selects and starts editing a node or link, we need to lock that graph object for all other users so they cannot interact with it until User A releases it. For those locked parts, we want them to become completely non-interactive for other users.

Our expected behavior for a locked node/link is:

  • The node/link should not be clickable/selectable.

  • Mouse enter/hover behavior should not trigger on the locked object.

  • The user should not be able to move/drag the locked node.

  • The user should not be able to delete the locked node/link using keyboard delete.

  • The user should not be able to draw a link from a locked node’s port.

  • The user should not be able to draw a link to a locked node’s port.

  • Custom actions such as replacing a node from the palette or dropping another node onto the locked node/link should also be avoided where possible.

We tried setting pickable: false on the locked node/GraphObjects, and it appears to work for many of our scenarios. The node becomes non-interactive, mouse enter does not fire, and keyboard delete is also blocked because the part cannot be selected.

I wanted to confirm whether pickable: false is the right approach for this use case, or whether there are any side effects we should be aware of.

Is pickable: false recommended when we want a locked node/link to be completely ignored by mouse/keyboard interactions? Or should we instead use a combination of properties like selectable, movable, deletable, copyable, fromLinkable, toLinkable, etc.?

Any guidance on the correct GoJS pattern for making RTC-locked parts read-only/non-interactive would be helpful.

Thanks!

Yes, I would set all the “…able” properties to false, rather than rely on pickable. Pickable would work for many tools, but probably not all. If something was already selected and copied, for instance, and then it was “locked” and a user tried to paste, pickable would not stop the paste.

We do have a GoJS with Yjs demo, which may be interesting for you to study, that we could publish soon.

Thanks Simon, that’s very helpful.
We’re currently building real-time collaboration for our diagram builder and are evaluating how to handle conflict resolution for concurrent operations — particularly cases like adjacent node deletions or two users inserting onto the same link simultaneously, where element-level locking alone doesn’t fully prevent conflicts. Understanding how Yjs integrates with GoJS’s model would be really valuable for us.
Is there any demo we can look into with Yjs? or could you share any early guidance on how the integration works?

You can see it here: Real-Time Collaborative Diagram Editor | GoJS Diagramming Library

You can find the source for the project here: GoJS-projects/gojs-collaborate at master · NorthwoodsSoftware/GoJS-projects · GitHub

This project is meant for study and as a proof-of-concept. There may be better ways to do things, but we are not Yjs experts, so its hard for us to say.

Thanks for sharing the demo and source, Simon! We’ll study the Yjs integration.

We have a specific conflict scenario we’re trying to solve, and we’d appreciate your thoughts on whether/how Yjs handles this:

Setup: Start → Node1 → Node2 → End

Our diagram has custom delete logic — when a node is deleted, its incoming links are automatically relinked to the next node in the chain (so the flow stays connected).

One after one edits (works fine):

  1. Client 1 deletes Node1 → diagram becomes Start → Node2 → End

  2. Client 2 receives the update, then deletes Node2 → diagram becomes Start → End

Concurrent edits (breaks):

  1. Client 1 deletes Node1 → locally sees Start → Node2 → End

  2. Client 2 (hasn’t received Client 1’s change yet) deletes Node2 → locally sees Start → Node1 → End

  3. When they exchange changes, the link retargeting logic produces dangling links on both sides — neither client ends up with a valid diagram, and they’re out of sync.

    How this can be solved? The problem is that our relinking logic is custom logic. Does the GoJS + Yjs handle this kind of scenarios?
    Any idea how this works? How can our custom logic works in this case.

This question is really outside the scope of GoJS, but I can give you some notes anyway.

First, if you’re not using Yjs already, you may not want to introduce it. Long term it may help your project to incorporate it, but it won’t help by itself with this particular issue.

The real problem is that when you are updating the clients, you are storing the consequence of the decision, rather than the decision. Then when you have two mutually incompatible decisions, everything breaks.

So if you’ve got nodes
A → B → C → D

And client 1 deletes B, you should basically send “B is marked deleted” to client 2. Don’t send other information about the model (like new linkings).

Then if Client 2 deletes C real quick

A → B → D
And gets an update
A → B (is deleted) → D

Client 2 can reconcile this graph. For any node marked “is deleted” it needs to update links and then delete it, so client 2 will correctly get:

A → D

then client 2 can send that C was deleted to client 1, so client 1 can do the same.

I hope I’m not simplifying too much, but that’s what I would do.