Custom component for text node including special characters
Hi @malonecj I'm not sure how exactly you would expect to see and manage those components (especially when you combine templates inside conditions) but I think your current approach in isComponent is not properly correct as you have to deal with text nodes. Inside your liquid handle isComponent you use el.textContent...
Read full answer below ↓Question
GrapesJS version
- I confirm to use the latest version of GrapesJS
What browser are you using?
Chrome v105
Reproducible demo link
https://codesandbox.io/s/fancy-brook-ppyc8v?file=/index.js
Describe the bug
Background: I am trying to configure grapesjs to be able to edit markup that contains the Liquid template language. As such I would like to create two types of custom components.
- A template block in the form of
{{some_variable}} - A condition block in the form of
{% if condition .... %}
The custom components should later be configured to allow the user to modify certain aspects of the components.
However, it appears that there is some issue in achieving the second requirement, having a code block that starts with {%}
How to reproduce the bug?
- Run the code sandbox link
- Observe that the custom component is initialised for the case of
liquid-template-blockbut not for the case of liquid-condition-block
What is the expected behavior? the initialisers for both custom components should be run
What is the current behavior?
only the initialiser for the custom component liquid-template-block is run
If is necessary to execute some code in order to reproduce the bug, paste it here below: just run the code sandbox link
Code of Conduct
- I agree to follow this project's Code of Conduct
Answers (3)
Hi @malonecj I'm not sure how exactly you would expect to see and manage those components (especially when you combine templates inside conditions) but I think your current approach in isComponent is not properly correct as you have to deal with text nodes.
Inside your liquid handle isComponent you use el.textContent which I think should be similar to liquid condition with el.nodeValue as it's only available on text nodes (probably it would be better to check if it's actually a text node el.nodeType === 3).
As a first step try to update your custom components in this way, as currently are skipped from the parser due to this condition
editor.Components.addType("liquid-condition", {
isComponent: (el) => {
const { nodeValue } = el;
const isComponent = nodeValue?.trim().includes("{%");
// This probably will help you understand better how the parser processes nodes
console.log('isComponent liquid-condition', { el, nodeValue, isComponent });
if (isComponent) {
return {
tagName: 'liquid-condition-el',
components: { type: 'textnode', content: nodeValue },
}
}
},
...
editor.Components.addType("liquid-handle", {
isComponent: (el) => {
const { nodeValue } = el;
const isComponent = nodeValue?.trim().includes("{{");
console.log('isComponent liquid-handle', { el, nodeValue, isComponent });
if (isComponent) {
return {
tagName: 'liquid-handle-el',
components: { type: 'textnode', content: nodeValue },
}
}
},
...
But I don't think this approach will work once you start to mix templates with conditions as from the HTML parser perspective you receive something like this:
* div
* textNode - containing content starting from '{% case...' to '{% endcase %}'
* div - containing 'Hello World!!!'
* textNode - containing `{{ some_content }}`
So probably another approach would be to use one custom component as a parser of the template engine
Components.addType("liquid-case-condition", {
// no need for isComponent here, only the parser will output components for the engine
model: {
toHTML(opts) {
return `{% case ${this.get('conditionName')} %}
${this.getInnerHTML(opts)}
{% endcase %}`;
}
},
});
// ... other components to handle better the template engine
Components.addType("template-engine", {
isComponent: (el) => {
const { nodeValue } = el;
if (nodeValue?.trim().includes("{%")) {
const parsedTextNode = parseEngineText(nodeValue);
// transform parsed stuff in components for the editor, eg.
return {
type: 'template-engine',
components: [
{
type: 'liquid-case-condition',
conditionName: 'shop.subscription.currentPlanName',
components: [
// ... other template engine components
]
}
// ...
]
};
}
},
});
I'm closing this one as there are no issues with the core itself, if you have any other questions @malonecj let me know.
Thanks for reporting this, @malonecj.
Great suggestion about Custom component for text node including special characters! While this specific feature isn't yet in the core API, there are several ways to achieve similar behavior.
Using the event system:
editor.on('component:update', (component) => {
// your logic here
});
Alternative approaches:
- Listen to
selector:addfor CSS selector changes - Use
selector:customfor custom rules - Tap into the
change:*events for fine-grained tracking - Build a plugin that extends the editor with this capability
Making it official: If this feature would benefit many users, consider opening a formal Feature Request on the GrapesJS repo with:
- A detailed use case
- Code example showing the desired behavior
- Why this matters for your workflow
The core team is receptive to well-motivated feature requests backed by real use cases.
Related Questions and Answers
Continue research with similar issue discussions.
Issue #3939
Disabled drag and drop in preview
GrapesJS version[X] I confirm to use the latest version of GrapesJSWhat browser are you using? v95Reproducible demo link https://codesandbo...
Issue #6651
Droppable is ignored when moving a textable component into text
GrapesJS version [x] I confirm to use the latest version of GrapesJS What browser are you using? Chrome 142.0.7444.61 Reproducible demo lin...
Issue #6081
Custom Component not able to visible in grapsJs/studio at first rendering
GrapesJS version[X] I confirm to use the latest version of GrapesJSWhat browser are you using? chromeReproducible demo link https://stackbl...
Issue #6032
Image Component allows upload and selection of video files via drag and drop
GrapesJS version [X] I confirm to use the latest version of GrapesJS What browser are you using? Chrome v126 Reproducible demo link https:/...
Paid Plugins That Match This Issue
Curated by issue keywords and label relevance to help you ship faster.
Loading paid plugin recommendations...
Check the open-source GrapesJS plugins on GitHub or run a quick search in our free catalog.
Browse free plugins →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.
Tutorial
Find the Right GrapesJS Plugin in Seconds: Smarter Discovery Is Live
We're shipping a set of discovery upgrades. New label filters, a proper compatibility switch for GrapesJS vs Studio, one-click and a smarter sort bar.
Tutorial
GrapesJS vs Webflow vs Tilda: What to Choose for Your Business in 2026
Choosing the right website platform in 2026 is no longer just about building a site
Tutorial
GJS Market 2.0 - Donations, Tracking, Labels and Better Product Discovery
We’ve rolled out a new set of GrapesJS marketplace updates across GJS Market, focused on improving how creators distribute products
Browse Plugin Categories
Jump directly to plugin category pages on the marketplace.