Issue #5379✓ SolvedOpened September 12, 2023by Sw33tgt4 reactions

Class modifications of components are not automatically detected

Quick answerby Sw33tgt2

For the moment, as a workaround, I added this in a editor.on('component:mount', (component) => {}); function until any feedback is received. It seems to be working well. In a case where a class is removed from the definition, the removed class will still be available on the saved component's class list, but I guess th...

Read full answer below ↓

Question

GrapesJS version

  • I confirm to use the latest version of GrapesJS

What browser are you using?

Chrome Version 116

Reproducible demo link

https://jsfiddle.net/h5b0ndvL/1/

Describe the bug

When a component is re-rendered in the canvas, a new attribute added to the model definition get rendered, but a newly added class doesn't get added when the component is rendered in the canvas. The initialize function of Component.ts add the defaults attributes as well as the component's attribute. this.set('attributes', { ...(result(this, 'defaults').attributes || {}), ...(this.get('attributes') || {}), });

However, when looking at the initClasses() function, I can only see the get('classes') from the component itself, but it doesn't compare with the default list ((result(this, 'defaults').classes) to check if it changed.

How to reproduce the bug?

  1. Create a component with predefined classes
  2. Drag the component on the page
  3. Save the page
  4. Change the classes definition of the component and add a new class to the model
  5. Change the attributes definition of the component and add a new attribute to the model
  6. Refresh the page to load the content again

What is the expected behavior? When a class/attributes for a component type is modified/added, the component must be re-rendered.

What is the current behavior? The newly class is not added to the model class list like it does for the attributes.

If is necessary to execute some code in order to reproduce the bug, paste it here below:

From the JsFiddle link, go to the blocks and drag the "Test Class" block to the canvas. Based on the original definition of the component, the class 'section' and the attributes 'data-type' should be added to the component:

image

I then add a new class and a new attribute to the component's model definition. After running the code again, the new attribute gets added to the component, but the new class doesn't get added to the component:

image

Here's a video of how to reproduce it based on my JsFiddle Link: https://github.com/GrapesJS/grapesjs/assets/144697108/3da7d308-a8f2-4405-8e81-fa1644bb6c9e

Code of Conduct

  • I agree to follow this project's Code of Conduct

Answers (4)

👍 Most helpfulSw33tgtSeptember 12, 2023

For the moment, as a workaround, I added this in a editor.on('component:mount', (component) => {}); function until any feedback is received. It seems to be working well. In a case where a class is removed from the definition, the removed class will still be available on the saved component's class list, but I guess there's no workaround around this.

editor.on('component:mount', (component) => {  
    const cls = component.getClasses() || [];
    const clsArr = __isString(cls) ? cls.split(' ') : cls;
    // Default Class
    const dCls = component.defaults.classes || [];
    // Temporary class array for comparison
    const tClsArr = clsArr.map((cl) => __isString(cl) ? cl : cl.name);
    // New class that is not in current tClsArray 
    const nClsArr = dCls.filter((x) => tClsArr.indexOf(x) === -1);

    if(nClsArr.length > 0) {
      // Found a missing class, add it to the component
      component.addClass(nClsArr.toString());
    }

    function __isString(x) {
      return typeof x === 'string';
    }
  });

For now, I have achieved my expected result, but any feedback would be appreciated. Thanks.

Sw33tgtSeptember 20, 2023

Yeah, this is intentional with classes, with your approach you won't be able to remove defined classes from the component, they will keep being added on component init.

Which is fine for when a class has been forgotten on a component and we want to add it in another version of the plugin on render. For us, we've set these classes to private, so they are not visible to the user and can't be removed manually.

I believe, as a workaround, I could store old classes definition to a new property and compare them before rendering the new class to see which one needs to be removed. I can work on a workaround that will work for us.

Thanks for your reply @artf,

Sw33tgtSeptember 12, 2023

Something like this in the initClasses() function of Component.ts would add the new classes to the element on render. Let me know what you think :

    const cls = this.get('classes') || attrCls || [];
    
    // changing from const to let
    let clsArr = isString(cls) ? cls.split(' ') : cls;

    /* Compare with default class  */
    const dCls = result(this, 'defaults').classes || [];
    const tClsArr = clsArr.map((cl: any) => isString(cl) ? cl : cl.name);
    const nClsArr = dCls.filter((x: any) => tClsArr.indexOf(x) === -1);
    if(nClsArr.length > 0) {
      clsArr = [].concat(clsArr, nClsArr);
    }

    this.stopListening(...toListen);
    const classes = this.normalizeClasses(clsArr);
    const selectors = new Selectors([]);
    this.set('classes', selectors, opts);
    selectors.add(classes);
    selectors.on('add remove reset', this.__upSymbCls);
    // Clear attributes from classes
    attrCls && classes.length && this.set('attributes', restAttr);
    // @ts-ignore
    this.listenTo(...toListen);
    return this;
  }
ClaudeCodeMay 17, 2026

Thanks for reporting this, @Sw33tgt.

Great question about Class modifications of components are not automatically detected. The recommended approach with Canvas is to use the event-driven API.

Start here:

  1. Check the GrapesJS documentation for your specific module
  2. Look for the on() event listener method
  3. Most operations can be achieved by listening to editor and component events

Common patterns:

// Listen for changes
editor.on('change', () => console.log('something changed'));

// Component lifecycle
editor.on('component:mount', (c) => console.log('component ready', c));
editor.on('component:update', (c) => console.log('component updated', c));

If you're still stuck:

  • Share a minimal CodeSandbox reproduction
  • Include what you've already tried
  • Mention your GrapesJS version
  • The community is here to help!

Related Questions and Answers

Continue research with similar issue discussions.

Paid Plugins That Match This Issue

Curated by issue keywords and label relevance to help you ship faster.

View all plugins

Loading paid plugin recommendations...

Free option

Check the open-source GrapesJS plugins on GitHub or run a quick search in our free catalog.

Browse free plugins →
Premium option

Premium plugins ship with support, regular updates, and production-ready features — save days of integration work.

Browse premium plugins →

Related tutorials

In-depth guides on the same topic.

All tutorials →

Browse Plugin Categories

Jump directly to plugin category pages on the marketplace.