Using Tabulator grids in Dashboard v2.0 templates

My team is using interactive data grids extensively in our dashboard UI. Due to limitations of ui-table in dashboard v1.0 & v2.0, we instantiate the Tabulator package (Tabulator) directly within v2.0 template nodes, and thus enjoy the full Tabulator functionality.
I developed a "Grid wrapper", which can manage multiple grids on a UI page, and supports the full Tabulator API (sync & async), receiving & sending flow messages & error handling in both single & multi user modes. It can also expose direct access to the Tabulator objects, allowing local (message-less) operations within the template (e.g. in button-click or onChange callbacks).

@joepavitt, my intention is to take this a step further into a custom dashboard node (which I'll be happy to share with the user community). I have developed many custom nodes, but not yet a dashboard node. Is there some stripped-down, hello-worldish prototype which I can take and build upon?

1 Like

Hi @omrid,
Nice to hear you consider developing a custom ui node for D2!

Here you can find the documentation for third party ui node development. And Joe has also been so kind to provide us an example ui node.

I also started to create a cheat sheet for ui node development in D2, but due to time limitations I have not been able to complete it yet. I created that diagram because some time ago I start migrating one of my ui nodes to D2, and (since I don't know anything about Vue) I had big troubles to see the cohesion between all the details in the documenation. So not sure if it can be of any help to you, but it was good enough for me to understand the complete picture.

Good luck!!

1 Like

@BartButenaers has already shared the best resources to help you get started - but do feel free to ping me or tag me in open PRs/repos as you build this out.

1 Like

OK, so I finally got around to try implementing Tabulator in a dashboard v2.0 node.
Following the advice from @BartButenaers & @joepavitt, I downloaded the example node, and managed to install & run it in my local environment.
As I understand from the documentation, unlike regular custom nodes, dashboard nodes need to be "compiled" to a .umd.js file which is then loaded to the dashboard.
I'm trying to experiment with the example node, but not able to perform this compilation. When I try to do npm run build, I get the following error:

What am I doing wrong?

On another note - my Tabulator implementation is not using Vue. Is it still necessary to include the Vue imports in the node, or can I try to remove them?

Did you do npm install in your node's root folder? Because that installs all dependencies from your package.json file, which includes Vite. If I remember correctly. But far from an expert...

Thanks. I always npm install from within my Node-red home directory. And the new example node did come up correctly. Was I supposed to install it on a different path?

No I don't mean about installing your new node so Node-RED can use it. That is something else.

I mean if you have a look at the package.json file of your ui node, you will see the Vite related libraries:


So if you are inside the folder of your ui node and you do npm install, the all these dependencies are installed. Which means you can afterwards do inside your ui node folder also npm run build, which then hopefully works because now Vite has been installed.

OK, this seems to have solved the issue. Thanks for the quick reply!
Moving forward...


The Dashboard framework does depend on each widget being a Vue component (which is built into the umd.js file you can see. So whilst it doesn't have to be Vue through and through, it does need to at least be wrapped in a Vue component.

OK, next question :slight_smile:
I send & receive Node-red messages successfully, following the example code:

    mounted () {
        this.$socket.on('msg-input:' +, (msg) => {
		     this.$socket.emit('widget-action',, {payload: "Forwarding: "+msg.payload})

        // tell Node-RED that we're loading a new instance of this widget
    unmounted () {
        /* Make sure, any events you subscribe to on SocketIO are unsubscribed to here */
        this.$socket?.off('widget-load' +
        this.$socket?.off('msg-input:' +

But how do I stop the original incoming message from being automatically forwarded to the output port? In ui-template there is a checkbox for this.

There is also for the Dashboard 2 template node (at the bottom)

1 Like

Hi @joepavitt

Can you help here. I looked at the Dashboard 2.0 template source code, and tried to set a passthru variable in various places, but still cannot eliminate the automatic msg passthrough.

As far as I know there is no automatic passthrough in ui-template. If you don't call send() or emit() then it will not send anything.

Thanks @Colin. In Dashboard 2.0 ui-template has a checkbox for this (added a few releases ago):
So no issues with ui-template nodes

Here I am trying to develop a new custom dashboard 2.0 node. The node is working well, but forwards all incoming messages automatically, in addition to any message I emit.
In the example below, I forward every incoming message, thus it comes out twice:

        this.$socket.on('msg-input:' +, (msg) => {
		    this.$socket.emit('widget-action',, {payload: "Forwarding: "+msg.payload})

Is your source available in a repository that we can look at.

Have you looked at how this is achieved in the template node?

Oh, I thought you were using ui-template, I see now that you are developing a new contrib node, so my comments are not relevant.

1 Like

Thanks @Steve-Mcl

I started from an example node
Which does the auto-forwarding.

Following your advice, I looked into how the passthrough is implemented in other Dashboard 2.0 nodes, and figured out that in my custom node, there is a send(msg) applied unconditionally upon the onInput event. When I comment it out, the passtrough gets disabled. Thanks!
I will set this as a node configuration option. Just wanted to ensure that I am not impacting any other required behavior when disabling the automatic send

You got it @omrid - just make sure that passthru is considered within your onInput.

I can't recall from memory if you can just use the in-built onInput function, and not provide your own, I think you can, but need to double check when at my desk on Tuesday. You should only need to define your own onInput function if you have bespoke behaviour that differs from the default.

Just to confirm, the definition of onInput in is optional in the evts object when running `group.register(node, config, evts)

If none is provided, then it will fall back to the default handler which does the following steps:

  • Save latest msg to server-side datastore
  • Append msg.topic if topic and topicType is a defined property on the node
  • Check on passthru field, forward on msg if appropriate

Source code for the default handler can be found: node-red-dashboard/nodes/config/ui_base.js at b8ef0be9b4e11a450a7c0c4cd5d2bedab601fd28 · FlowFuse/node-red-dashboard · GitHub

And even better is the documentation where I detail the .register function: Widget Registration | Node-RED Dashboard 2.0