Panels and Buttons section of getting started not working with Vue.js
Take a look at this issue, maybe helps: https://github.com/artf/grapesjs/issues/275
Read full answer below ↓Question
I am working to get grapejs to work with Vue.js and am having no luck, I am following the getting started however I am unable to get it to work properly. So far I have added it to the mounted() section and have gotten some buttons with blocks to show up and add in however I have had no luck getting the Panels and Buttons section to work.
Issue TLDR: Using Vue.js I can't get the panels and buttons section from getting started to show.
Below I have included my entire App.vue file which is the main one I am trying to add it into at this time - proof of concept will move to a proper location later.
<template>
<div class="base">
<div class="panel__top">
<div class="panel__basic-actions"></div>
</div>
<div id="gjs">
<h1>Hello World Component!</h1>
</div>
<div id="blocks"></div>
<link rel="stylesheet" href="//unpkg.com/grapesjs/dist/css/grapes.min.css">
</div>
</template>
<script>
// import VueGrapesjs from 'vue-grapesjs'
import grapesjs from 'grapesjs'
export default {
name: 'app',
data () {
return {
}
},
mounted(){
grapesjs.init({
// Indicate where to init the editor. You can also pass an HTMLElement
container: '#gjs',
// Get the content for the canvas directly from the element
// As an alternative we could use: `components: '<h1>Hello World Component!</h1>'`,
fromElement: true,
// Size of the editor
height: '1000px',
width: 'auto',
// Disable the storage manager for the moment
storageManager: false,
// Avoid any default panel
panels: { defaults: [] },
// Allows creation of "blocks"
blockManager: {
appendTo: '#blocks',
blocks: [
{
id: 'section', // id is mandatory
label: '<b>Section</b>', // You can use HTML/SVG inside labels
attributes: { class:'gjs-block-section' },
content: `<section>
<h1>This is a simple title</h1>
<div>This is just a Lorem text: Lorem ipsum dolor sit amet</div>
</section>`,
}, {
id: 'text',
label: 'Text',
content: '<div data-gjs-type="text">Insert your text here</div>',
}, {
id: 'image',
label: 'Image',
// Select the component once it's dropped
select: true,
// You can pass components as a JSON instead of a simple HTML string,
// in this case we also use a defined component type `image`
content: { type: 'image' },
// This triggers `active` event on dropped components and the `image`
// reacts by opening the AssetManager
activate: true,
}
]
},
});
grapesjs.Panels.addPanel({
id: 'panel-top',
el: '.panel__top',
});
grapesjs.Panels.addPanel({
id: 'basic-actions',
el: '.panel__basic-actions',
buttons: [
{
id: 'visibility',
active: true, // active by default
className: 'btn-toggle-borders',
label: '<u>B</u>',
command: 'sw-visibility', // Built-in command
}, {
id: 'export',
className: 'btn-open-export',
label: 'Exp',
command: 'export-template',
context: 'export-template', // For grouping context of buttons from the same panel
}, {
id: 'show-json',
className: 'btn-show-json',
label: 'JSON',
context: 'show-json',
command(editor) {
editor.Modal.setTitle('Components JSON')
.setContent(`<textarea style="width:100%; height: 250px;">
${JSON.stringify(editor.getComponents())}
</textarea>`)
.open();
},
}
],
});
}
}
</script>
<style>
#gjs {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
border: 3px solid #444;
}
/* Reset some default styling */
.gjs-cv-canvas {
top: 0;
width: 100%;
height: 100%;
}
.gjs-block {
width: auto;
height: auto;
min-height: auto;
}
.panel__top {
padding: 0;
width: 100%;
display: flex;
position: initial;
justify-content: center;
justify-content: space-between;
}
.panel__basic-actions {
position: initial;
}
</style>
Answers (4)
Take a look at this issue, maybe helps: https://github.com/artf/grapesjs/issues/275
<template>
<div :id="id"></div>
</template>
<script>
export default {
props: {
id: {
type: String,
required: true
}
},
data() {
return {
editor: null
}
},
methods: {
change() {
this.$emit('change', this.editor.getHtml());
},
},
mounted() {
this.editor = grapesjs.init({
container: '#editor',
height: this.height,
plugins: ['gjs-preset-newsletter'],
});
this.editor.on('change', this.change);
}
}
</script>
Hi @joshk132 honestly the code looks good, are you able to create a reproducible demo (on something like Codesandbox)?
@artf I've gotten it working where the panels show up however it now doesn't display right. The solution I had to do was change to use the below code. Pretty much I had to have "editor" as a variable which I had removed in my initial code because the vue linter was complaining. Attached there is a link to an image of how it looks with the below code, I am unsure on what exactly is wrong to cause the style issues.
<template>
<div class="base">
<div class="panel__top">
<div class="panel__basic-actions"></div>
<div class="panel__switcher"></div>
</div>
<div class="panel__right">
<div class="layers-container"></div>
<div class="styles-container"></div>
</div>
<div id="gjs">
<h1>Hello World Component!</h1>
</div>
<div id="blocks"></div>
<link rel="stylesheet" href="https://unpkg.com/grapesjs/dist/css/grapes.min.css">
</div>
</template>
<script>
// import VueGrapesjs from 'vue-grapesjs'
import grapesjs from 'grapesjs'
export default {
name: 'app',
data () {
return {
}
},
mounted(){
const editor = grapesjs.init({
// Indicate where to init the editor. You can also pass an HTMLElement
container: '#gjs',
// Get the content for the canvas directly from the element
// As an alternative we could use: `components: '<h1>Hello World Component!</h1>'`,
fromElement: true,
// Size of the editor
height: '1000px',
width: 'auto',
// Disable the storage manager for the moment
storageManager: false,
// Allows creation of "blocks"
blockManager: {
appendTo: '#blocks',
blocks: [
{
id: 'section', // id is mandatory
label: '<b>Section</b>', // You can use HTML/SVG inside labels
attributes: { class:'gjs-block-section' },
content: `<section>
<h1>This is a simple title</h1>
<div>This is just a Lorem text: Lorem ipsum dolor sit amet</div>
</section>`,
}, {
id: 'text',
label: 'Text',
content: '<div data-gjs-type="text">Insert your text here</div>',
}, {
id: 'image',
label: 'Image',
// Select the component once it's dropped
select: true,
// You can pass components as a JSON instead of a simple HTML string,
// in this case we also use a defined component type `image`
content: { type: 'image' },
// This triggers `active` event on dropped components and the `image`
// reacts by opening the AssetManager
activate: true,
}
]
},
layerManager: {
appendTo: '.layers-container'
},
});
editor.Panels.addPanel({
id: 'panel-top',
el: '.panel__top',
});
editor.Panels.addPanel({
id: 'basic-actions',
el: '.panel__basic-actions',
buttons: [
{
id: 'visibility',
active: true, // active by default
className: 'btn-toggle-borders',
label: '<u>B</u>',
command: 'sw-visibility', // Built-in command
}, {
id: 'export',
className: 'btn-open-export',
label: 'Exp',
command: 'export-template',
context: 'export-template', // For grouping context of buttons from the same panel
}, {
id: 'show-json',
className: 'btn-show-json',
label: 'JSON',
context: 'show-json',
command(editor) {
editor.Modal.setTitle('Components JSON')
.setContent(`<textarea style="width:100%; height: 250px;">
${JSON.stringify(editor.getComponents())}
</textarea>`)
.open();
},
}
],
});
editor.on('run:export-template:before', opts => {
console.log('Before the command run');
opts.abort = 0; // 0 = true 1 = false
});
editor.on('run:export-template', () => console.log('After the command run'));
editor.on('abort:export-template', () => console.log('Command aborted'));
}
}
</script>
<style>
#gjs {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
border: 3px solid #444;
}
/* Reset some default styling */
.gjs-cv-canvas {
top: 0;
width: 100%;
height: 100%;
}
.gjs-block {
width: auto;
height: auto;
min-height: auto;
}
.panel__top {
padding: 0;
width: 100%;
display: flex;
position: initial;
justify-content: center;
justify-content: space-between;
}
.panel__basic-actions {
position: initial;
}
.editor-row {
display: flex;
justify-content: flex-start;
align-items: stretch;
flex-wrap: nowrap;
height: 300px;
}
.editor-canvas {
flex-grow: 1;
}
.panel__right {
flex-basis: 230px;
position: relative;
overflow-y: auto;
}
</style>
Thanks for reporting this, @joshk132.
Great suggestion about Panels and Buttons section of getting started not working with Vue.js! 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 #3231
how to change gjs-ckeditor-format component
hi guys, we all know that gjs-ckeditor-plugin working with [data-gjs-type]="text" components. I wanna know , could we change this option? I...
Issue #3392
FEAT: Allow dynamic droppable configuration using a function
I have a requirement where I cannot use selectors to determine if an element is droppable. For example: The "child" component must be withi...
Issue #3280
FEAT: Renderless
What are you trying to add to GrapesJS? I try to styling GrapesJS and positioning its elements (panels, buttons, canvas, etc.), it's annoyi...
Issue #3223
FEAT: Create the canvas from a URL
I've been playing with GrapesJS and trying to integrate it into a Symfony 5 project. In particular I want to be able to edit templates in t...
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 →Browse Plugin Categories
Jump directly to plugin category pages on the marketplace.