# Symbols

WARNING

This feature is released as a beta from GrapesJS v0.21.11

To get a better understanding of the content in this guide we recommend reading Components first

Symbols are a special type of Component that allows you to easily reuse common elements across your project. They are particularly useful for components that appear multiple times in your project and need to remain consistent. By using Symbols, you can easily update these components in one place and have the changes reflected everywhere they are used.

# Concept

A Symbol created from a component retains the same shape and uses the same Components API, but it includes a reference to other related Symbols. When you create a new Symbol from a Component, it creates a Main Symbol, and the original component becomes an Instance Symbol.

When you reuse the Symbol elsewhere, it creates new Instance Symbols. Any updates made to the Main Symbol are automatically replicated in all Instance Symbols, ensuring consistency throughout your project.

Below is a simple representation of the connection between Main and Instance Symbols.

Note

This feature operates at a low level, meaning there is no built-in UI for creating and managing symbols. Developers need to implement their own UI to interact with this feature. Below you'll find an example of implementation.

# Programmatic usage

Let's see how to work with and manage Symbols in your project.

# Create symbol

Create a new Symbol from any component in your project:

const anyComponent = editor.getSelected();
const symbolMain = editor.Components.addSymbol(anyComponent);

This will transform anyComponent to an Instance and the returned symbolMain will be the Main Symbol. GrapesJS keeps track of Main Symbols separately in your project JSON, and they will be automatically reconnected when you reload the project.

The addSymbol method also handles the creation of Instances. If you call it again by passing symbolMain or anyComponent, it will create a new Instance of symbolMain.

const secondInstance = editor.Components.addSymbol(symbolMain);

Now, symbolMain references two instances of its shape.

To get all the available Symbols in your project, use getSymbols:

const symbols = editor.Components.getSymbols();
const symbolMain = symbols[0];

# Symbol details

Once you have Symbols in your project, you might need to know when a Component is a Symbol and get details about it. Use the getSymbolInfo method for this:

// Details from the Main Symbol
const symbolMainInfo = editor.Components.getSymbolInfo(symbolMain);

symbolMainInfo.isSymbol; // true; It's a Symbol
symbolMainInfo.isRoot; // true; It's the root of the Symbol
symbolMainInfo.isMain; // true; It's the Main Symbol
symbolMainInfo.isInstance; // false; It's not the Instance Symbol
symbolMainInfo.main; // symbolMainInfo; Reference to the Main Symbol
symbolMainInfo.instances; // [anyComponent, secondInstance]; Reference to Instance Symbols
symbolMainInfo.relatives; // [anyComponent, secondInstance]; Relative Symbols

// Details from the Instance Symbol
const secondInstanceInfo = editor.Components.getSymbolInfo(secondInstance);

symbolMainInfo.isSymbol; // true; It's a Symbol
symbolMainInfo.isRoot; // true; It's the root of the Symbol
symbolMainInfo.isMain; // false; It's not the Main Symbol
symbolMainInfo.isInstance; // true; It's the Instance Symbol
symbolMainInfo.main; // symbolMainInfo; Reference to the Main Symbol
symbolMainInfo.instances; // [anyComponent, secondInstance]; Reference to Instance Symbols
symbolMainInfo.relatives; // [anyComponent, symbolMain]; Relative Symbols

# Overrides

When you update a Symbol's properties, changes are propagated to all related Symbols. To avoid propagating specific properties, you can specify at the component level which properties to skip:

anyComponent.set('my-property', true);
secondInstance.get('my-property'); // true; change propagated

anyComponent.setSymbolOverride(['my-property']);
// Get current override value: anyComponent.getSymbolOverride();

anyComponent.set('my-property', false);
secondInstance.get('my-property'); // true; change didn't propagate

# Detach symbol

Once you have Symbol instances you might need to disconnect one to create a new custom shape with other components inside, in that case you can use detachSymbol.

editor.Components.detachSymbol(anyComponent);

const info = editor.Components.getSymbolInfo(anyComponent);
info.isSymbol; // false; Not a Symbol anymore

const infoMain = editor.Components.getSymbolInfo(symbolMain);
infoMain.instances; // [secondInstance]; Removed the reference

# Remove symbol

To remove a Main Symbol and detach all related instances:

const symbolMain = editor.Components.getSymbols()[0];
symbolMain.remove();

# Events

The editor triggers several symbol-related events that you can leverage for your integration:

  • symbol:main:add Added new root main symbol.
editor.on('symbol:main:add', ({ component }) => { ... });
  • symbol:main:update Root main symbol updated.
editor.on('symbol:main:update', ({ component }) => { ... });
  • symbol:main:remove Root main symbol removed.
editor.on('symbol:main:remove', ({ component }) => { ... });
  • symbol:main Catch-all event related to root main symbol updates.
editor.on('symbol:main', ({ event, component }) => { ... });
  • symbol:instance:add Added new root instance symbol.
editor.on('symbol:instance:add', ({ component }) => { ... });
  • symbol:instance:remove Root instance symbol removed.
editor.on('symbol:instance:remove', ({ component }) => { ... });
  • symbol:instance Catch-all event related to root instance symbol updates.
editor.on('symbol:instance', ({ event, component }) => { ... });
  • symbol Catch-all event for any symbol update (main or instance).
editor.on('symbol', () => { ... });

# Example

Below is a basic UI implementation leveraging the Symbols API: