Issue with Multiple Topology Renderings on Tab Switch in Node-RED Dashboard

I am working on a Node-RED flow where I have a ui-template node rendering a network topology using Next.js. The topology is displayed when navigating to the /topo1 page. However, I am facing an issue when switching between tabs (e.g., page1 to topo1 or page2 to topo1). Each time I navigate to /topo1, the topology renders again below the previous instance, leading to multiple instances of the topology being displayed on the same page.

Here is a summary of my flow:

  • A ui-template node is used to render the topology.
  • Navigation is handled using ui-event and switch nodes to detect the active tab and render the appropriate content.

Steps I have tried:

  • I checked the flow logic and ensured the nodes are correctly wired.
  • Verified the ui-template node's configuration, ensuring it is scoped to the page.

What I need help with:

  • How can I prevent multiple instances of the topology from being rendered when switching to the /topo1 tab?
  • Is there a way to clear or reset the ui-template content when switching tabs?
[
    {
        "id": "ff48d7ee859f6e3f",
        "type": "debug",
        "z": "a738176e45f8f666",
        "name": "debug 2587",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "false",
        "statusVal": "",
        "statusType": "auto",
        "x": 550,
        "y": 200,
        "wires": []
    },
    {
        "id": "65960a3bd9b90826",
        "type": "ui-template",
        "z": "a738176e45f8f666",
        "group": "",
        "page": "2ca7ac6b39b2c442",
        "ui": "",
        "name": "topo_trial",
        "order": 1,
        "width": "10",
        "height": "6",
        "head": "",
        "format": "<template>\n  <h1>Topology</h1>\n  <div id=\"topology-container\"></div>\n</template>\n<link rel=\"alternate icon\" href=\"./favicon.ico\" type=\"image/png\" sizes=\"16x16\">\n<!-- <link rel=\"stylesheet\" href=\"./nextjs/next.css\"> -->\n    <link rel=\"stylesheet\" href=\"./assets/nextjs/next.css\">\n<script src=\"./assets/nextjs/next.js\"></script>\n<script>\n  (function(nx){\nconst topologyData = {\nnodes: [\n// ISPs\n{ id: 0, x: 400, y: -100, name: \"ISP1\", device_type: \"cloud\", color: \"grey\" },\n{ id: 1, x: 600, y: -100, name: \"ISP2\", device_type: \"cloud\", color: \"grey\" },\n\n// Routers\n{ id: 2, x: 400, y: 0, name: \"Edge1\", device_type: \"router\", color: \"red\" },\n{ id: 3, x: 600, y: 0, name: \"Edge2\", device_type: \"router\", color: \"red\" },\n\n// Switches\n{ id: 4, x: 400, y: 100, name: \"Switch1\", device_type: \"switch\" },\n{ id: 5, x: 600, y: 100, name: \"Switch2\", device_type: \"switch\" },\n\n// Servers\n{ id: 6, x: 200, y: 200, name: \"ESX1\", device_type: \"server\" },\n{ id: 7, x: 400, y: 200, name: \"ESX2\", device_type: \"server\" },\n{ id: 8, x: 600, y: 200, name: \"ESX3\", device_type: \"server\" },\n{ id: 9, x: 800, y: 200, name: \"ESX4\", device_type: \"server\" },\n\n// SAN\n{ id: 10, x: 500, y: 300, name: \"SAN\", device_type: \"server\" }\n],\nlinks: [\n// WAN to routers\n{ source: 0, target: 2, color: \"green\" },\n{ source: 1, target: 3 },\n\n// Routers to switches\n{ source: 2, target: 4, color: \"green\" },\n{ source: 2, target: 5 },\n{ source: 3, target: 4 },\n{ source: 3, target: 5 },\n\n// Switches to Switches\n{ source: 4, target: 5 },\n{ source: 4, target: 5 },\n\n// Servers to Switches\n{ source: 6, target: 4, color: \"green\" },\n{ source: 6, target: 5, color: \"red\" },\n{ source: 7, target: 4, color: \"green\" },\n{ source: 7, target: 5, color: \"red\" },\n{ source: 8, target: 4, color: \"green\" },\n{ source: 8, target: 5, color: \"red\" },\n{ source: 9, target: 4, color: \"green\" },\n{ source: 9, target: 5, color: \"red\" },\n\n// SAN to Switches\n{ source: 10, target: 4, color: \"red\" },\n{ source: 10, target: 4, color: \"red\" },\n{ source: 10, target: 5, color: \"red\" },\n{ source: 10, target: 5, color: \"red\" }\n]\n};\n// instantiate next app\nconst app = new nx.ui.Application();\n\n// configuration object\nconst topologyConfig = {\n// configuration for nodes\nwidth: window.innerWidth,\nheight: window.innerHeight,\nnodeConfig: {\nlabel: \"model.name\",\niconType: \"model.device_type\",\ncolor: \"model.color\",\n},\n// configuration for links\nlinkConfig: {\nlinkType: \"straight\",\ncolor: \"model.color\"\n},\n// if true, the nodes' icons are shown, a dot is shown instead\nshowIcon: true,\n};\n\n// instantiate Topology class\nconst topology = new nx.graphic.Topology(topologyConfig);\n\n// load topology data from app/data.js\ntopology.data(topologyData);\n\n// bind the topology object to the app\ntopology.attach(app);\n\n// app must run inside a specific container. In our case this is the one with id=\"topology-container\"\napp.container(document.getElementById(\"topology-container\"));\n\n})(nx);\n</script>\n<style>\n  /* define any styles here - supports raw CSS */\n</style>",
        "storeOutMessages": true,
        "passthru": true,
        "resendOnRefresh": true,
        "templateScope": "widget:page",
        "className": "nextjs",
        "x": 380,
        "y": 200,
        "wires": [
            [
                "ff48d7ee859f6e3f"
            ]
        ]
    },
    {
        "id": "4dfe62d884f54b12",
        "type": "ui-event",
        "z": "a738176e45f8f666",
        "ui": "64fc71361e24a0d0",
        "name": "",
        "x": 110,
        "y": 200,
        "wires": [
            [
                "4710c8b6b890847b"
            ]
        ]
    },
    {
        "id": "4710c8b6b890847b",
        "type": "switch",
        "z": "a738176e45f8f666",
        "name": "",
        "property": "payload.page.path",
        "propertyType": "msg",
        "rules": [
            {
                "t": "eq",
                "v": "/topo1",
                "vt": "str"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 1,
        "x": 230,
        "y": 200,
        "wires": [
            [
                "65960a3bd9b90826"
            ]
        ]
    },
    {
        "id": "2ca7ac6b39b2c442",
        "type": "ui-page",
        "name": "topo1",
        "ui": "64fc71361e24a0d0",
        "path": "/topo1",
        "icon": "home",
        "layout": "grid",
        "theme": "default",
        "breakpoints": [
            {
                "name": "Default",
                "px": "0",
                "cols": "3"
            },
            {
                "name": "Tablet",
                "px": "576",
                "cols": "6"
            },
            {
                "name": "Small Desktop",
                "px": "768",
                "cols": "9"
            },
            {
                "name": "Desktop",
                "px": "1024",
                "cols": "12"
            }
        ],
        "order": -1,
        "className": "",
        "visible": "true",
        "disabled": "false"
    },
    {
        "id": "64fc71361e24a0d0",
        "type": "ui-base",
        "name": "",
        "path": "/dashboard",
        "appIcon": "",
        "includeClientData": true,
        "acceptsClientConfig": [
            "ui-iframe",
            "ui-template",
            "ui-gauge",
            "ui-chart",
            "ui-slider",
            "ui-form",
            "ui-text-input",
            "ui-number-input",
            "ui-file-input",
            "ui-button",
            "ui-button-group",
            "ui-dropdown",
            "ui-radio-group",
            "ui-switch",
            "ui-text",
            "ui-chart",
            "ui-form",
            "ui-number-input",
            "ui-switch",
            "ui-table",
            "ui-gauge",
            "ui-markdown",
            "ui-iframe",
            "ui-tabulator",
            "ui-radio-group",
            "ui-dropdown",
            "ui-button-group",
            "ui-file-input",
            "ui-notification",
            "ui-control"
        ],
        "showPathInSidebar": false,
        "showPageTitle": false,
        "navigationStyle": "icon",
        "titleBarStyle": "fixed"
    }
]