Skip to main content

JointJS - JsPlumb Comparison

· 7 min read
Simon Porritt
JsPlumb core team

One question we hear a lot from prospective licensees is how does JsPlumb compare as an alternative to JointJS? The people at JointJS have taken several stabs at summarizing this but the results can at best be described as an exercise in creative writing.

In today's post I'm going to jot down a few thoughts of our own, plus those of the many developers who come to us once they've reached the limit of JointJS's capabilities.

What, then, are the key differentiators between JsPlumb and JointJS? We can think of many, but these are what we consider the key ones:

JsPlumb is not limited to SVG but JointJS is

With JsPlumb you can use any valid HTML or SVG to render the elements in your UI - you are not limited to just SVG, which, while useful for a certain class of UIs such as diagrams, quickly becomes a nightmare to work with when you want something a little more bespoke. This was the topic of a recent post of ours, and it's quite fundamental. If you can build it in JointJS you can, naturally, build it in JsPlumb, because HTML is a superset of SVG in the browser. But you cannot say the opposite: there are plenty of UIs that you can build with JsPlumb that are difficult, if not impossible, to build with JointJS.

So, with JsPlumb you can build a UI in which some of your elements are plain old SVG:

<svg viewBox="0 0 173.2 200" width="80" height="80">
<path fill="forestgreen"
d="M86.6 0L173.2 50L173.2 150L86.6 200L0 150L0 50Z"></path>
</svg>

and some of them are arbitrarily complex HTML:

<div class="demo-html" data-cow="show">
<strong>{{label}}</strong>
<select>
<option value="show">Show cow</option>
<option value="hide">Hide cow</option>
</select>

<input type="text" placeholder="enter label"/>
</div>

Notice the cow? You can hide and show that cow. One great advantage of HTML over SVG nodes is that you can use all the power of CSS. We use it here to hide and show the cow:


.my-node {
background:none;
}

[data-cow='show'] {
background-image: url('/img/351032562.jpg');
background-size: cover;
}

When the user makes a selection in the drop down, we simply update the data-cow attribute on the node element.

We've also put a nice box shadow on that node, via CSS. When you build applications with JsPlumb it's much easier to take advantage of all of the underlying technologies.

JsPlumb integrates with Angular, React, Vue 3 and Svelte. JointJS does not.

This blog is a Docusaurus application, meaning it runs on React, and I can embed React components into the page. For instance, how about this <ShowMeACow/> component here - try clicking this link:

This is a functional React component, and obviously a very useful one. The source is:

import React, {useState } from "react"

export default function ShowMeACow({size, startShown}) {

const s = size || 450
const [showCow, setShowCow] = useState(startShown === true)

return <div className="show-me-a-cow">
<button onClick={() => setShowCow(!showCow)}>{showCow ? "Hide that" : "Show me a"} cow</button>
{showCow && <img src="/img/351032562.jpg" width={s}/>}
</div>
}

So what do I mean when I say JsPlumb integrates with React? I mean I can just use JsPlumb's React integration and put that ShowMeACow component right inside a node:


I wrote a component:

const CowComponent = function({ctx}) {
return <div className="show-me-a-cow-demo-node">
<ShowMeACow size={150} startShown={ctx.vertex.data.cowShown}/>
</div>
}

and then I mapped it inside my Toolkit view:

const view = {
nodes: {
default: {
jsx: (ctx) => <CowComponent ctx={ctx}/>
}
}
}

And then I used the SurfaceComponent from our React integration to draw that canvas above. In one of my nodes I set cowShown:true and in the other one I didn't.

It is not possible to do these things with JointJS, and whilst this charming cow is perhaps a spurious example, it's not hard to see how powerfully rich your UI can be when you can integrate to this level.

JointJS will tell you their library can be integrated with React, for example, but if we take a look at the integration guide, we can see that what they mean by "integration" is not integration as discussed above, but rather just instantiating Joint inside a useEffect :

import { useEffect, useRef } from 'react';
import { dia, ui, shapes } from '@joint/plus';
import './App.css';

function App() {

const canvas = useRef<Element | null>(null);

useEffect(() => {
const graph = new dia.Graph();

const paper = new dia.Paper({
model: graph,
background: {
color: '#F8F9FA',
},
frozen: true,
async: true,
cellViewNamespace: shapes
});

...
})

return (
<div className="canvas" ref={canvas}/>
);

That's not integration. True integration, in which a library such as React, Angular, Vue or Svelte is used to render the elements of your UI, and you can have a palette that you render with arbitrary JSX from which you can drag new elements onto your canvas, or you can drop in a controls component for the library you're using, or one of many other fully integrated helper components, is not offered by JointJS. It is offered by JsPlumb though, and we offer this for Angular, React, Vue 3 and Svelte.

JsPlumb has no external dependencies. JointJS has 3. And more if you want touch events.

To get JointJS to do anything at all you have to include 3 external dependencies:

  • jQuery
  • Backbone
  • Lodash

These are all quite antiquated technologies now, and they do not come from the modern reactive world. The last lodash release was in 2016!

JsPlumb has no external dependencies: everything is tightly integrated, and if we wanted to update some core piece of our library we wouldn't have to wait for an external entity to do it.

This dependency on external libraries in JointJS actually extends to support touch events, surprisingly. In the Touch Gestures demo, the question is asked "Have you been wondering how to implement touch gestures such as pan and pinch in your JointJS application?", and the answer given is "use this third party library". I mean obviously I paraphrased that, but I would do that, since I am here representing JsPlumb, which has no external dependencies and also the smoothest pinch to zoom for miles around.

JsPlumb ships with component testing support

With JsPlumb you can use the jsPlumbToolkitTestHarness to run model-level component testing on your applications:


import { jsPlumbToolkitTestHarness, newInstance } from "@jsplumbtoolkit/browser-ui"

const toolkit = newInstance()
const surface = toolkit.render(...)

const tks = new jsPlumbToolkitTestHarness(toolkit, surface)
tks.dragConnection(["1", ".connect"], "2")
tks.dragVertexBy("1", 250, 0)
tks.tapOnNode("2")

Try it out here:

The component test harness integrates seamlessly with Angular, Vue, React and Svelte. JointJS doesn't ship with anything useful for testing like this.

Summary

Those are 4 key differences between JointJS and JsPlumb, and we of course consider each one of them on its own to be sufficient reason to choose JsPlumb. If you can build it with JointJS you can build it with JsPlumb - faster, and cheaper, probably.

Here's a matrix of our own:

JointJSJsPlumb
True integration with Angular, React, Vue and Svelte
Depends on multiple external libraries
Supports touch events
Full text search
Dialogs module
Object inspectors
Custom layouts
Edit edge paths
Transactions
Render dynamic selections
Component testing support
Access to unminified source code
No restrictions on use of license
Magnetizer - prevent elements overlapping
Graph Layout Algorithms
Dedicated support available
Export to SVG, PNG and JPG

Start a free trial

Sending your evaluation request...

Interested in the concepts discussed in this article? Start a free trial and see how JsPlumb can help bring your ideas to market in record time.


Get in touch!

If you'd like to discuss any of the ideas/concepts in this article we'd love to hear from you - drop us a line at hello@jsplumbtoolkit.com.