GrapesJS custom components guide (2026)

Define custom GrapesJS components with DomComponents.addType: detection with isComponent, a model with traits and defaults, and a view for canvas behavior.

DevFuture Development
DevFuture Development
Jun 18, 2026 β€’ 10 days ago
6 min read4 views

Components define behavior, not just markup

A GrapesJS component is a typed model with defaults, traits (editable settings), and an optional view. You register one with DomComponents.addType. This guide builds a custom component with a trait and shows how imported HTML maps onto it.

1. Register a component type

editor.DomComponents.addType('rating', {
  // Map matching HTML onto this type when content is loaded.
  isComponent: (el) => el.classList?.contains('rating') && { type: 'rating' },

  model: {
    defaults: {
      tagName: 'div',
      classes: ['rating'],
      // Editable settings shown in the trait panel:
      traits: [
        { type: 'number', name: 'stars', label: 'Stars', min: 1, max: 5 },
      ],
      stars: 5,
    },
  },
});

2. React to trait changes in a view

editor.DomComponents.addType('rating', {
  model: {
    defaults: { tagName: 'div', classes: ['rating'], stars: 5,
      traits: [{ type: 'number', name: 'stars', min: 1, max: 5 }] },
  },
  view: {
    init() {
      this.listenTo(this.model, 'change:stars', this.render);
    },
    onRender() {
      const n = this.model.get('stars');
      this.el.textContent = 'β˜…'.repeat(n) + 'β˜†'.repeat(5 - n);
    },
  },
});

3. Expose it as a block

editor.BlockManager.add('rating', {
  label: 'Rating',
  category: 'Widgets',
  content: { type: 'rating', stars: 4 },
});

Because isComponent recognises .rating elements, loading saved HTML re-creates the component β€” traits and behavior intact.

Common mistakes

Abstract code on a screen
isComponent detection and trait change events are the usual gotchas.

Custom components fail in predictable ways. If your isComponent doesn't return a truthy value (usually { type }) for matching DOM, loaded HTML won't map onto your type and traits disappear on reload. If the view doesn't listenTo the model's change:<trait> event, edits in the trait panel won't re-render. Re-rendering inside a handler that itself triggers a change can cause an infinite loop β€” update the DOM directly instead of calling a full render where possible. And set draggable/droppable deliberately so your component can only be placed where it makes sense.

Prerequisites

You need a running GrapesJS editor and basic JavaScript. Components are registered with DomComponents.addType; the three pieces are detection (isComponent), the model (defaults + traits), and an optional view (canvas behaviour).

Model: defaults, traits, and constraints

The model defines what the component is. Beyond traits, you can constrain where it can go and what it accepts with draggable, droppable, and editable:

editor.DomComponents.addType('rating', {
  isComponent: (el) => el.classList?.contains('rating') && { type: 'rating' },
  model: {
    defaults: {
      tagName: 'div',
      classes: ['rating'],
      droppable: false,
      traits: [{ type: 'number', name: 'stars', min: 1, max: 5 }],
      stars: 5,
    },
  },
});

View: react to trait changes

view: {
  init() { this.listenTo(this.model, 'change:stars', this.render); },
  onRender() {
    const n = this.model.get('stars');
    this.el.textContent = 'β˜…'.repeat(n) + 'β˜†'.repeat(5 - n);
  },
}

Why isComponent matters

isComponent runs when HTML is loaded into the editor. Returning the type object for matching elements is what lets saved markup re-hydrate into your component with its traits and behaviour intact β€” without it, a reload turns your component back into plain HTML.

Best practices

Keep the model declarative and put behaviour in the view. Avoid triggering a full render inside a change handler that itself causes a change β€” update the DOM directly to prevent loops. Set draggable/droppable deliberately so a component can only be placed where it makes sense. Expose meaningful traits so editors can configure the component without code.

Next steps

Surface components as draggable units in the custom blocks guide, bundle them in a custom plugin, or browse GrapesJS plugins on GJS.Market.

FAQ

How do I create a custom component in GrapesJS?

Call editor.DomComponents.addType(type, { isComponent, model, view }) β€” defining detection, defaults/traits, and canvas behavior.

What are traits?

Traits are the editable settings shown in the trait panel for a selected component; declare them in model.defaults.traits.

How does isComponent work?

It receives a DOM element and returns a truthy value (often { type }) when that element should become your component, so imported HTML maps onto your type.

Published Jun 18, 2026
Updated Jun 27, 2026
πŸ”Œ GJS.Market

Looking for GrapesJS plugins?

Over 100 curated plugins, presets, and templates β€” hand-picked for quality and maintained by the community.

Share this postTwitterFacebookLinkedIn
Published via
DevFuture Development
DevFuture Development
Visit shop β†’

More from DevFuture Development

Discover other insightful posts and stay updated with the latest content.

View all posts

Premium plugins from DevFuture Development

Hand-picked paid additions crafted by this creator.

Visit shop β†’