# Ui-page dynamic class

**URL:** <https://discourse.nodered.org/t/ui-page-dynamic-class/99918>\
**Category:** General\
**Tags:** dashboard-2\
**Created:** [11 December 2025 01:52 UTC](https://discourse.nodered.org/t/ui-page-dynamic-class/99918 "2025-12-11T01:52:13Z")\
**Posts on this page:** 10\
**Page:** 1

<div class="post-metadata">

**Author:** ![jbudd](https://avatars.discourse-cdn.com/v4/letter/j/5f8ce5/32.png) [@jbudd](https://discourse.nodered.org/u/jbudd)\
**Post date:** [11 December 2025 01:52 UTC](https://discourse.nodered.org/t/ui-page-dynamic-class/99918/1 "2025-12-11T01:52:13Z")

</div>

You can give a widget a custom class "foo":

 ![image](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/3X/c/e/ce3537aa1a65173a02544c1bc9813b884d8fbb81.png)  
And in dev tools you can see  
 ![image](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/3X/1/d/1dd4b9a44cd9d7aeae4a31012958f6b5bf03c360.png)

Inject `ui_update = {"class": "bar"}`, then you see  
 ![image](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/3X/4/4/44fad3cb71ac0371ed7b22bc2ce0a52237a45958.png)

And with a ui-page, you can set a custom class "foo"

 ![image](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/3X/5/1/51e7ea9c16c8228bef449ce5f28a24cef94cd27f.png)  
and the dev tools shows it  
 ![image](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/3X/d/7/d72b859b3887efbbd3333b9716fa74c7adccfdc7.png)

I cannot find a way via the ui control node to dynamically append a class and get `.nrdb-ui-page.foo.bar`

Is it me, by design or by oversight?

---

<div class="post-metadata">

**Author:** ![Steve-Mcl](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/steve-mcl/32/4826_2.png) [@Steve-Mcl](https://discourse.nodered.org/u/Steve-Mcl)\
**Post date:** [11 December 2025 07:15 UTC](https://discourse.nodered.org/t/ui-page-dynamic-class/99918/2 "2025-12-11T07:15:35Z")

</div>

The class attribute on a page is simply static. a means of dynamic control was never implemented.

Please feel free to raise a feature request issue as it is a worthy feature.

In the mean time, to get past this limitation, you could add a ui-template and do this is using client-side script.

---

<div class="post-metadata">

**Author:** ![omrid](https://avatars.discourse-cdn.com/v4/letter/o/77aa72/32.png) [@omrid](https://discourse.nodered.org/u/omrid)\
**Post date:** [11 December 2025 10:36 UTC](https://discourse.nodered.org/t/ui-page-dynamic-class/99918/3 "2025-12-11T10:36:46Z")

</div>

As a temporary workaround, wouldn't it be possible to set a class to the page programmatically in a template node?

---

<div class="post-metadata">

**Author:** ![jbudd](https://avatars.discourse-cdn.com/v4/letter/j/5f8ce5/32.png) [@jbudd](https://discourse.nodered.org/u/jbudd)\
**Post date:** [11 December 2025 10:43 UTC](https://discourse.nodered.org/t/ui-page-dynamic-class/99918/4 "2025-12-11T10:43:14Z")

</div>

> [@omrid](#):
>
> set a class to the page programmatically in a template node?

Can you give an example @omrid, msg.payload -\> page class? TY.

---

<div class="post-metadata">

**Author:** ![omrid](https://avatars.discourse-cdn.com/v4/letter/o/77aa72/32.png) [@omrid](https://discourse.nodered.org/u/omrid)\
**Post date:** [11 December 2025 11:37 UTC](https://discourse.nodered.org/t/ui-page-dynamic-class/99918/5 "2025-12-11T11:37:22Z")

</div>

In a template node belonging to this page, get the page's DOM element and add/remove/toggle the class:

```auto
msg.type = "setClass";
msg.payload = "dark-mode";
//-------------------------

// upon receiving the msg in the template node
if (msg.type === 'setClass') // identifies the msg 
{
   // find the DOM element of the page
   const element = document.querySelector("body");

   // Manipulate the page class
   element.classList.add(msg.payload); // or classList.remove() or classList.toggle()
}

```

I haven't tested this yet. Let me know if it works.

---

<div class="post-metadata">

**Author:** ![Steve-Mcl](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/steve-mcl/32/4826_2.png) [@Steve-Mcl](https://discourse.nodered.org/u/Steve-Mcl)\
**Post date:** [11 December 2025 11:55 UTC](https://discourse.nodered.org/t/ui-page-dynamic-class/99918/6 "2025-12-11T11:55:15Z")

</div>

i would use `topic` for action (`add`, `remove`, `toggle`) as they match up with the dom function  
And use `payload` for the class that you want to apply/remove

also, set something `msg.selector = '.page1'` // target page class or page id or something

```js
const op = msg.topic // the operation to perform e.g. add, remove, toggle
if (['add', 'remove', 'toggle'].includes(op)) {
   const element = document.querySelector(msg.selector || 'body')
   element.classList[op](msg.payload)
}

```

_untested_

### Important:

Dont forget to set the ui-template class to `d-none` ([ref](https://vuetifyjs.com/en/styles/display/#usage)) so it is not shown in your dashboard.

### Also important

This code ☝ would be typically in a watch handler watching the `msg` object so that when you send a msg to the ui-template, it operates

---

<div class="post-metadata">

**Author:** ![jbudd](https://avatars.discourse-cdn.com/v4/letter/j/5f8ce5/32.png) [@jbudd](https://discourse.nodered.org/u/jbudd)\
**Post date:** [11 December 2025 12:03 UTC](https://discourse.nodered.org/t/ui-page-dynamic-class/99918/7 "2025-12-11T12:03:50Z")

</div>

I put @omrid's code in a template `<script>` section and sent a message with type="setClass" and payload = "darkmode" but I don't see `<body class="darkmode">`

Sorry @Steve-Mcl, that all goes way over my head!

---

<div class="post-metadata">

**Author:** ![Steve-Mcl](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/steve-mcl/32/4826_2.png) [@Steve-Mcl](https://discourse.nodered.org/u/Steve-Mcl)\
**Post date:** [11 December 2025 12:56 UTC](https://discourse.nodered.org/t/ui-page-dynamic-class/99918/8 "2025-12-11T12:56:46Z")

</div>

> [@jbudd](#):
>
> Sorry @Steve-Mcl, that all goes way over my head!

```auto
[{"id":"13c18235c926694c","type":"ui-template","z":"d1c96cf8529a5579","group":"","page":"","ui":"83289142e2632cc2","name":"class-setter","order":0,"width":0,"height":0,"head":"","format":"<template>\n <div>\n </div>\n</template>\n\n<script>\n export default {\n watch: {\n // watch for any changes of \"count\"\n msg: function () {\n const m = this.msg || {}\n const {selector, topic, payload} = m // extract interesting bits\n console.log('got a msg with topic, payload, selector:', topic, payload, selector)\n // if payload is something and topic is one of add, remove or toggle, its good!\n if (payload && ['add', 'remove', 'toggle'].includes(topic)) {\n const element = document.querySelector(selector || 'body')\n element.classList[topic](payload)\n }\n }\n }\n }\n</script>\n","storeOutMessages":true,"passthru":true,"resendOnRefresh":true,"templateScope":"widget:ui","className":"d-none","x":910,"y":120,"wires":[[]]},{"id":"6b317a885c9e49ee","type":"inject","z":"d1c96cf8529a5579","name":"add class test to .page1","props":[{"p":"topic","vt":"str"},{"p":"payload"},{"p":"selector","v":".page1","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"add","payload":"test","payloadType":"str","x":600,"y":160,"wires":[["13c18235c926694c"]]},{"id":"36b14836d14f0ae6","type":"inject","z":"d1c96cf8529a5579","name":"remove class test from .page1","props":[{"p":"topic","vt":"str"},{"p":"payload"},{"p":"selector","v":".page1","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"remove","payload":"test","payloadType":"str","x":620,"y":200,"wires":[["13c18235c926694c"]]},{"id":"161cd2f7fac6b53b","type":"inject","z":"d1c96cf8529a5579","name":"toggle class test on .page1","props":[{"p":"topic","vt":"str"},{"p":"payload"},{"p":"selector","v":".page1","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"toggle","payload":"test","payloadType":"str","x":610,"y":120,"wires":[["13c18235c926694c"]]},{"id":"907bb2f87c6d201d","type":"ui-template","z":"d1c96cf8529a5579","group":"","page":"","ui":"83289142e2632cc2","name":"styles","order":0,"width":0,"height":0,"head":"","format":".test {\n display: inline-block; \n font-size: 30px;\n -webkit-transform: matrix(-1, 0, 0, 1, 0, 0);\n -moz-transform: matrix(-1, 0, 0, 1, 0, 0);\n -o-transform: matrix(-1, 0, 0, 1, 0, 0);\n transform: matrix(-1, 0, 0, 1, 0, 0);\n}\n","storeOutMessages":true,"passthru":true,"resendOnRefresh":true,"templateScope":"site:style","className":"","x":890,"y":80,"wires":[[]]},{"id":"83289142e2632cc2","type":"ui-base","name":"My Dashboard","path":"/dashboard","includeClientData":true,"acceptsClientConfig":["ui-notification","ui-control"],"showPathInSidebar":false,"headerContent":"page","navigationStyle":"default","titleBarStyle":"default","notificationDisplayTime":5}]

```

![chrome_RyfQHDWmJE](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/3X/1/2/12ac3268877d50cdb9caa05c551f2484174c8e8c.gif)

---

<div class="post-metadata">

**Author:** ![jbudd](https://avatars.discourse-cdn.com/v4/letter/j/5f8ce5/32.png) [@jbudd](https://discourse.nodered.org/u/jbudd)\
**Post date:** [11 December 2025 13:45 UTC](https://discourse.nodered.org/t/ui-page-dynamic-class/99918/9 "2025-12-11T13:45:35Z")

</div>

Ahah! I had to work out to set a custom class of `page1` for my page.

 ![image](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/3X/0/0/00c0788285e1d657ee9935138f5a932c5529c12f.png)

 ![image](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/3X/7/1/71acf1235a2e5428a5364a877c17ca6b6a2fc257.png)

Thanks!

---

<div class="post-metadata">

**Author:** ![system](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/1X/d073cd938eafa2e558d7c2cd59003b3ef4963033.png) [@system](https://discourse.nodered.org/u/system)\
**Post date:** [25 December 2025 13:45 UTC](https://discourse.nodered.org/t/ui-page-dynamic-class/99918/10 "2025-12-25T13:45:42Z")

</div>

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.
