Integrating GrapesJS into a Vue 3 App — Complete Guide for 2025

Build a Vue 3 landing page builder with GrapesJS. Drag-and-drop UI, custom blocks, local storage, and full integration guide for 2025

DevFuture Development
DevFuture Development
October 31, 20257 days ago
By DevFuture
33 min read324 views

Introduction

Building a drag-and-drop landing page builder in Vue 3 is made significantly easier with GrapesJS. GrapesJS is an open-source web builder framework that lets you visually design HTML templates without coding. It comes with a rich feature set out of the box, including:

  • Drag-and-drop blocks (both built-in and custom)
  • Device previews via a Device Manager (for responsive design)
  • A Style Manager and Layer Manager for fine-tuning components
  • A robust Plugin system to extend functionality

In this guide, we’ll integrate GrapesJS into a Vue 3 project (using Webpack) to create a landing page builder. We assume you have intermediate knowledge of Vue.js and basic understanding of Webpack or Vue CLI. We'll walk through setting up the project, installing GrapesJS, and incrementally adding features: mounting the editor, integrating Vue components, creating custom blocks, and implementing save/load functionality. We’ll also discuss how to structure your code for maintainability and dive into advanced tips like theming the GrapesJS editor, managing storage (including backend integration), extending the editor with plugins/commands, and ensuring responsive design. Let’s get started!


Project Setup (Vue 3 + Webpack)

First, set up a Vue 3 project. If you haven't already, you can use the Vue CLI (which uses Webpack under the hood) or manually configure Webpack. For simplicity, we'll use Vue CLI here:

  1. Install Vue CLI: Run npm install -g @vue/cli to install the Vue CLI globally (ensure you have Node.js and npm installed). You can verify installation by checking the version: vue --version.
  2. Create a Vue 3 App: Use the CLI to scaffold a new project:
vue create grapesjs-vue-app

Choose the default preset (which includes Vue 3 and Babel; Vue CLI will set up Webpack for you). This will generate a project in the grapesjs-vue-app directory.

3. Run the Development Server: Navigate into the project folder (cd grapesjs-vue-app) and start the dev server: npm run serve. By default, the app will be served at http://localhost:8080. You should see the default Vue welcome page


Now you have a basic Vue 3 app running. We’ll remove the default content and prepare a blank canvas for GrapesJS:


  • Open src/App.vue and remove the boilerplate template (the default markup and <HelloWorld> component). This leaves App.vue mostly empty, aside from the root <template> and an empty <script> and <style> (you can keep a basic style if needed).
  • Create a new component to contain the GrapesJS editor. For example, create src/components/PageBuilder.vue (or WebBuilder.vue) with the basic component structure: a template, script, and style section.

Tip: It’s good practice to dedicate a component to the page builder. This keeps GrapesJS logic isolated. We’ll use <PageBuilder> to encapsulate the editor.


Next, let's install GrapesJS and set up this component.

Installing GrapesJS and Preparing the Editor

Inside your Vue project, install the GrapesJS library and its default plugin for web pages:

npm install grapesjs grapesjs-preset-webpage

This installs two packages: the core GrapesJS library, and the "Webpage Preset" plugin which provides a set of ready-made blocks (text, images, videos, forms, etc.) suitable for building landing pages. The preset will save us time by providing common blocks and configurations out-of-the-box.

Now, open your PageBuilder.vue component. We will import GrapesJS and its assets, then initialize the editor. Here's a step-by-step setup for the component:

<!-- src/components/PageBuilder.vue -->
<template>
 <!-- Container for GrapesJS editor -->
 <div ref="editorContainer" id="gjs"><!-- GrapesJS canvas will mount here --></div>
</template>

<script setup>
import { ref, onMounted } from 'vue';
import grapesjs from 'grapesjs';
import 'grapesjs/dist/css/grapes.min.css';       // GrapesJS core CSS
import 'grapesjs-preset-webpage/dist/grapesjs-preset-webpage.min.css'; // Plugin CSS
// Import plugin script (ensures it's registered)
import 'grapesjs-preset-webpage';

const editorContainer = ref(null);

onMounted(() => {
 // Initialize GrapesJS editor after component is mounted
 const editor = grapesjs.init({
  container: editorContainer.value, // Mount to the ref div
  height: '100%', 
  width: '100%',
  // Optionally, use an existing element's HTML as a starting point:
  fromElement: false, // false because we will start with a blank canvas
  // Storage Manager configuration (we'll cover saving/loading later)
  storageManager: {
   id: 'gjs-',      // Prefix for local storage keys
   type: 'local',    // Store locally
   autosave: true,    // Auto-save changes
   autoload: true,    // Auto-load saved data on init
   stepsBeforeSave: 1  // Save after each change (for demo; adjust in production)
   // storeComponents, storeStyles, etc., are true by default in recent versions
  },
  // Include the preset plugin for basic blocks
  plugins: ['gjs-preset-webpage'],
  pluginsOpts: {
   'gjs-preset-webpage': {} // Using default options for now
  }
 });
});
</script>

<style>
 /* Ensure the editor canvas fills its container */
 #gjs {
  min-height: 800px; /* for example, give it some height */
 }
</style>


Let's break down what’s happening here:

We import GrapesJS and its CSS. The CSS is crucial for the editor’s styling (panels, canvas highlights, etc.). We also import the preset plugin's CSS for block styling. By importing the plugin’s JS (grapesjs-preset-webpage), we make sure it's registered so that referencing it by name works.


We use a <div id="gjs" ref="editorContainer"></div> as the container where the GrapesJS editor will be mounted. We give it an id "gjs" (not strictly required, but many examples use #gjs as the container selector).


In the <script setup> (Composition API), we define a ref for the container and call grapesjs.init inside an onMounted hook. This is important – the GrapesJS initialization should run after the component is mounted, so the container div is in the DOM. If you attempt to init earlier, the ref might be null and the editor won't render.


The grapesjs.init({...}) configuration:

  • container: We pass the actual DOM element (editorContainer.value) where GrapesJS will render. You could also use a selector like container: '#gjs' since we have an ID, but using the ref ensures it targets this component’s element.
  • height and width: We set the editor canvas size. Here it's 100% width of its container and a minimum height (800px in CSS). You can adjust these or even make height dynamic.
  • fromElement: We set this to false to start with a blank canvas. If you had some starter HTML inside the div#gjs, you could set fromElement: true to load that into the editor In our case, we want an empty page by default.
  • Storage Manager: We configure storage so that GrapesJS will save the user's progress. We use type: 'local' to enable saving to localStorage (the browser's storage). We also enable autosave and autoload so that every change is saved and reloaded automatically
  • Plugins: We include the 'gjs-preset-webpage' plugin which adds a set of basic blocks (headers, text blocks, images, columns, etc.) and default configurations
  • We leave pluginsOpts empty (using default plugin options). Later, you can customize plugin options (for example, the preset allows enabling a flexbox-based grid system, custom block categories, etc.).

After this setup, if you import and render <PageBuilder> in your App (e.g., include it in App.vue template), you should see the GrapesJS editor appear in the browser once the component mounts. You’ll get the GrapesJS canvas in the middle and panels around it for dragging blocks, styling, and so on. At this point, you effectively have a basic page builder running in your Vue app!


Tip: Make sure to import the PageBuilder component in your App.vue (or wherever you want the editor) and include it in the template. For example:

<template>
 <PageBuilder />
</template>
<script>
import PageBuilder from '@/components/PageBuilder.vue';
export default { components: { PageBuilder } }
</script>

This will display the GrapesJS editor as the main content. For actual applications, you might load the editor on a specific route or within an admin interface rather than on the homepage.

Integrating Vue Components with GrapesJS

One common question is how to use your existing Vue components inside the GrapesJS canvas. For instance, if you have a custom Vue component (say a <PricingTable> or <ContactForm> component), can you make it available as a drag-and-drop block in GrapesJS? The short answer: yes, but with some workarounds. GrapesJS is framework-agnostic and outputs static HTML/CSS, so it doesn’t natively understand Vue components. However, there are a few strategies to integrate Vue-driven content:

  • Use Custom Blocks with Placeholders: The simplest approach is to create a GrapesJS block that inserts a placeholder element or HTML for your component. For example, you could add a block that, when dropped, adds <div class="my-pricing-table">[Pricing Table will load here]</div> to the canvas. This placeholder can have an ID or class that you later replace with an actual Vue component. In a real app, after the user saves the page design, you could post-process the saved HTML, find these placeholders, and mount your Vue components in those spots (or replace the placeholder divs with your component's HTML via server-side rendering). Essentially, GrapesJS would be used to design the layout, and your Vue app takes over to hydrate or replace parts of the output with live components.
  • Convert Vue Components to Web Components: Vue 3 can compile components into custom elements (web components). You could compile a Vue component as a custom element (using defineCustomElement) and then register that in the page. GrapesJS would treat <my-component> as just another HTML tag (it won't know it's Vue, but the browser will, if the custom element JS is included). This way, when the block is rendered in the GrapesJS canvas (which uses an iframe for the canvas by default), the custom element's logic runs. This approach requires you to bundle your component as a web component and ensure GrapesJS’s canvas includes the script to define it.
  • Directly Mount Vue in GrapesJS Components: For more advanced use, GrapesJS allows custom component types with scripts. You can define a custom component type in GrapesJS that, on render, programmatically mounts a Vue component. For example, using GrapesJS’s Component API, you might add a component type that, when its element is inserted or updated, calls createApp(Component).mount(element). This is complex because it involves injecting Vue runtime into the GrapesJS canvas and managing it, but it’s a technique used by some integrations. (One community project, for instance, provides Vue Composition API hooks for GrapesJS to help with such integrations

For most intermediate use cases, treating Vue components as static blocks (the first approach) is often sufficient. It lets designers arrange components, and developers can later replace placeholders with real components. If your goal is a true WYSIWYG for dynamic components inside GrapesJS, be prepared for a more advanced setup.


Note: GrapesJS’s strength is in building static HTML structures. If your Vue components are primarily static (just markup and style), you can also convert them into GrapesJS native components or blocks by simply using their rendered HTML as block content. If they have interactive behavior, you'll need to ensure that behavior (JavaScript) loads in the GrapesJS preview or after the page is built.


In summary, integrating Vue components into GrapesJS is possible but can be complex. For this guide, we'll focus on using GrapesJS to build static content layouts (since that covers most landing page builders). We will, however, show how to add custom blocks next, which is the first step toward including custom content (Vue-based or otherwise) in the editor.

Creating Custom Blocks in GrapesJS

GrapesJS comes with a set of basic blocks (especially with the webpage preset plugin). These include text blocks, images, video embeds, columns, etc. However, you'll likely want to add your own custom blocks specific to your application (e.g., a pre-styled hero section, a pricing table layout, a call-to-action section, etc.). Custom blocks make it easy for users to drag in pre-built design elements.

To add a custom block, you use GrapesJS’s Block Manager API. You can access it via the editor.Blocks (or editor.BlockManager) module. For example, to add a simple block:

// Assuming `editor` is your GrapesJS instance
editor.Blocks.add('hero-section', {
 label: 'Hero Section',
 category: 'Sections',
 attributes: { class: 'fa fa-header' }, // icon for the block (using a FontAwesome class here)
 content: `
  <section class="hero-section" style="padding: 100px; text-align: center; background: #f5f5f5;">
   <h1>Hero Title</h1>
   <p>Subheading goes here</p>
  </section>
 `
});

This code gives the GrapesJS editor a new block with ID "hero-section". It will appear in the blocks panel with the label "Hero Section" (under a category "Sections"). We specified an icon using a FontAwesome class for visual aid. The content can be a string of HTML (as shown above) or a GrapesJS component JSON definition. Here we used a simple HTML string for clarity. When the user drags this block onto the canvas, the defined HTML structure will be added.


You can call such block-adding code right after initializing GrapesJS. For instance, in our onMounted, after grapesjs.init returns the editor, you can do:

// ... after editor = grapesjs.init({...});
editor.Blocks.add('hero-section', { ... });
editor.Blocks.add('testimonial', { ... });
// etc.

Make sure to add blocks after the editor is initialized (or in a GrapesJS plugin if you write one). The GrapesJS docs confirm that you can use editor.Blocks.add(id, definition) to programmatically add blocks.


You might also want to remove or reorganize the default blocks. The webpage preset offers options to configure which blocks to include. For example, in our pluginsOpts we could limit or modify the blocks from the preset. In the code from the Esketchers tutorial, they showed how to disable certain default blocks by specifying the blocks array in the preset options. For instance:

pluginsOpts: {
 'gjs-preset-webpage': {
  blocksBasicOpts: {
   blocks: ['column1','column2','column3','text','image','video'], // only include these
   flexGrid: 1, // enable flexgrid for column resizing
  },
  blocks: ['link-block','quote','text-basic'] // additional blocks to include
 }
}

You can tailor this to include or exclude blocks as needed. Alternatively, you can simply hide unwanted blocks via CSS or remove them via the Block Manager API (editor.Blocks.remove('block-id')).

Customizing block categories: It's a good idea to group blocks into categories (like "Sections", "Media", "Forms", etc.) for usability. The GrapesJS default blocks have categories already, and you can specify category when adding your block (as we did with "Sections" for the hero). GrapesJS will list blocks under their categories in the block panel.

At this stage, your landing page builder can have a mix of built-in blocks and custom blocks. Users can drag elements onto the canvas and compose a page.


Structuring Your Code for Maintainability

As you integrate GrapesJS into your Vue app, it’s important to keep the code organized and maintainable. Here are some best practices and architectural tips:

  • Encapsulate the Editor in a Component: We did this with <PageBuilder>. Keep all GrapesJS-related setup (imports, initialization, custom block definitions, etc.) inside this component or a set of related components. This prevents GrapesJS (which is fairly large and global) from polluting the rest of your app. If the page builder is only one part of your application (e.g., an admin tool), you might even lazy-load this component to avoid loading GrapesJS until needed.
  • Use the Composition API or Composables: If you prefer separation of concerns, you could create a custom Vue composable (e.g., useGrapesJS) that handles initializing the editor and returns the editor instance and perhaps reactive state for things you care about. In fact, there’s a community project Vue GrapesJS Composables that provides such hooks, giving reactive access to GrapesJS data inside Vue. This can help in creating a custom UI around GrapesJS (for example, controlling the editor via Vue).
  • Organize Custom Blocks and Components: As your library of custom blocks grows, consider storing their definitions in separate modules or JSON files. For example, you might have a blocks/ directory with files like heroBlock.js, featureBlock.js exporting functions that take an editor and add the block. Then import and use those in your PageBuilder component. This makes it easier to maintain and test blocks individually.
  • Avoid Tight Coupling with Vuex/Pinia: GrapesJS has its own internal state (the canvas content). Instead of trying to mirror every change in a Vuex store, use GrapesJS’s events to sync what you need. For instance, you can listen to editor.on('component:add', ...) or editor.on('storage:store', ...) if you want to trigger something in your app (like alert "saved"). But it's often sufficient to treat the GrapesJS content as black box managed by GrapesJS until you're ready to save it.
  • Clean up if Needed: If your page builder component can be mounted and unmounted repeatedly (say the user navigates away), you might want to destroy the GrapesJS instance on unmount to avoid memory leaks. You can do editor.destroy() on the beforeUnmount hook.
  • Keep Styling Concerns Separate: GrapesJS comes with a default CSS for the editor UI. If you override it or add custom styles for the canvas elements, ensure you scope those appropriately (perhaps via the Style Manager or via global CSS that targets the .gjs- classes). Try not to mix GrapesJS’s editor styles with your Vue app’s styles to avoid conflicts.

By following these practices, you can integrate a powerful page builder without making your overall project hard to maintain. The key is to treat GrapesJS as a sub-module of your app with clear boundaries.


Advanced Tips and Techniques

Now that we have the core integration working, let's explore some advanced tips to get the most out of GrapesJS in your Vue app:

1. Theming the GrapesJS Editor UI

Out of the box, GrapesJS has a clean light-themed interface. But you might want it to match your app’s branding or dark mode. To customize the editor UI, you have a few options:


Custom CSS: GrapesJS’s UI elements have specific classes (e.g., .gjs-pn-views for panels, .gjs-block for blocks, etc.). You can override these in your own CSS. For example, to create a dark theme, you might override background and text colors of panels. The GrapesJS documentation references a theming guide, and essentially you include your CSS after the GrapesJS CSS to override styles. Keep in mind future GrapesJS updates might slightly change class names, so test your overrides on upgrade.

Rebuilding the UI Layout: GrapesJS allows you to render parts of the UI into your own HTML containers. For instance, you can render the block list, style manager, trait manager, etc., into custom panels you've created. This is done by specifying appendTo in the editor config for those managers. For example:

blockManager: { appendTo: '#my-blocks-panel' },
styleManager: { appendTo: '#my-styles-panel' },
traitManager: { appendTo: '#my-traits-panel' },

If you create corresponding <div id="my-blocks-panel"> in your page layout, GrapesJS will insert the UI there. Using this technique, you could design a completely custom sidebar or toolbar in your Vue template and let GrapesJS fill in the functionality. Essentially, GrapesJS can be used headless in terms of UI – you decide where each part goes. This requires more work, but it's powerful for integrating into an existing design system.


Icons and Assets: GrapesJS uses Font Awesome 5 for some default icons (like component handles, etc.). If those don't load (e.g., due to content security or offline usage), you might need to include FA or replace icons with your own via the configuration. Check that the icons appear; if not, include a link to FA CSS or swap in text labels.


Quick example – custom panel colors: If you want to quickly change the look, you could do something like:

/* Dark theme example */
.gjs-pn-panel, .gjs-frame-wrapper, .gjs-layer-manager {
 background-color: #2b2b2b !important;
 color: #eee !important;
}
.gjs-pn-btn {
 color: #eee !important;
}

This would make panels dark gray and text light. Use !important because GrapesJS has inline styles on some elements. Fine-tuning will require more selectors, but it's doable.

2. Extending the Editor with Plugins and Commands

GrapesJS is highly extensible. We've already used the webpage preset plugin. There are many community plugins available for things like integrating rich text editors (e.g., CKEditor or Quill for better text editing within GrapesJS), asset managers (for image uploads), code editors, etc. You can find these on NPM or the GrapesJS website.

To use a plugin, you typically install it via npm and then add it to the plugins array in config (similar to how we did with preset). For example, to integrate CKEditor for rich text, you'd do:


import gjsCKEditor from 'grapesjs-plugin-ckeditor';
...
plugins: ['gjs-preset-webpage', gjsCKEditor],
pluginsOpts: {
 gjsCKEditor: { /* CKEditor options */ }
}

This replaces the default text editor with CKEditor within GrapesJS.

You can also write custom plugins if you have functionality to reuse. A GrapesJS plugin is essentially a function that takes the editor instance and typically adds commands, blocks, or extends components. If you find yourself adding the same custom blocks or commands in multiple places, consider refactoring them into a plugin.


Custom commands: GrapesJS includes many built-in commands (for example, basic commands for styling, undo/redo, etc.). You can add your own commands via editor.Commands.add('my-command', { run(editor, sender, options) { ... } }). For instance, you could add a command to export the HTML and CSS, or a command to clear the canvas, etc. You can then tie these commands to buttons in the UI (either custom buttons you add to GrapesJS panels, or even external UI elements via calling editor.runCommand('my-command')).


Example: Add a command to clear the canvas:

editor.Commands.add('canvas-clear', {
 run: editor => {
  editor.DomComponents.clear();
  editor.CssComposer.clear();
  localStorage.removeItem('gjs-project'); // if using project storage, clear it
 }
});

Then you might add a button in GrapesJS panel config:

editor.Panels.addButton('options', [{ // 'options' is a default panel id
 id: 'clear',
 className: 'fa fa-trash',
 command: 'canvas-clear',
 attributes: { title: 'Clear Canvas' }
}]);

This would show a trash icon in the options panel to clear the page.


3. Responsive Design Considerations

One of GrapesJS’s strengths is letting you build responsive pages. The built-in Device Manager (the top bar that often shows Desktop, Tablet, Mobile toggles) lets users preview and adjust styles for different device sizes. In our configuration, we included the default devices. We can also customize or add devices. For example, the code snippet from earlier added a 'Tablet' size and a specific 'Mobile (Portrait)' size with defined pixel widths.


Ensure your custom blocks and layout structures are built with responsiveness in mind. The preset webpage plugin includes a Flexbox-based grid system (enabled by flexGrid: 1 in the plugin options). This allows users to create columns that are flexible and stack on smaller screens. If you included that (the preset by default uses it if enabled), your column blocks will be using CSS flexbox for responsiveness. If not, you might rely on plain <table> or static percentage widths.


GrapesJS allows setting different CSS for different device modes. When a user switches the device in the editor, any style changes they make are applied as media-query-specific CSS. For instance, if you select the Mobile view and change a text size, GrapesJS will create a media rule for that. This means the exported CSS will contain the media queries.


A good practice is to define a mobile breakpoint and a desktop breakpoint in the Device Manager and let intermediate sizes just inherit. By default, GrapesJS uses max-width media queries for devices (desktop has no max, tablet might be max-width 992px, mobile 575px, etc., as seen in the example). You can invert this to min-width if you prefer mobile-first CSS, but the default works fine for most.


Finally, always test the pages you build with GrapesJS on actual devices or using your browser's responsive mode to ensure the output is truly responsive. GrapesJS provides the tools, but it's up to your block designs and CSS to make the end result responsive.


Tip: Encourage your users (or remember yourself) to use the device toggle buttons while editing. GrapesJS (with the preset) shows Desktop, Tablet, Mobile icons by default. If you don't see them, you can enable them via the Device Manager config or add them as custom panel buttons. Using these, one can switch to Mobile view and adjust content (like hide some blocks on mobile or change font sizes) to ensure the landing page looks good on all screens.


Conclusion

Integrating GrapesJS into a Vue 3 application opens up a world of possibilities: you can empower end-users or team members to visually design pages (landing pages, email templates, dashboards, etc.) right within your app. We covered how to set up a basic landing page builder with drag-and-drop, using Vue 3 and Webpack as the backbone.


To recap, we started by scaffolding a Vue project and installing GrapesJS with the webpage preset plugin. We created a dedicated component to mount the GrapesJS editor and configured it with local storage autosave for instant persistence. We then discussed how to add custom blocks to tailor the editor to our use case (a key step in turning GrapesJS into your page builder). We looked at saving and loading mechanisms, highlighting GrapesJS's built-in local storage and how to hook it up to a remote API for real data storage.


We also tackled important architecture and maintenance considerations, ensuring the integration doesn't turn into a tangled mess. By isolating GrapesJS in a component and possibly leveraging Vue's composition API, you keep your codebase clean and extensible.


In the advanced section, we explored theming the editor UI to match an enterprise's branding, using GrapesJS’s configuration to restructure or recolor the interface. We delved into storage nuances for multi-page scenarios and stressed the ease of extending GrapesJS with an ecosystem of plugins or custom commands to fit bespoke needs. Finally, we emphasized responsive design – because any page builder in 2025 must handle mobile and various devices – and how GrapesJS facilitates that through its Device Manager and style tools.


Use Cases: With this setup, you can build internal tools like email template builders, product landing page designers for your marketing team, or even customer-facing page builders as part of a SaaS (think custom microsite builders, form designers, etc.). Enterprises can integrate GrapesJS to let non-developers safely modify page layouts within set boundaries. Students and hobbyists can use it to quickly mock up interfaces and learn about DOM structure and styling in a visual way.


Next Steps: If you plan to use this in production, consider tying the save functionality into your backend or a headless CMS – for example, saving the GrapesJS JSON or HTML to a database and then rendering it on your actual website or app. You might also integrate a user authentication system to have multi-user support and asset storage for images (GrapesJS’s Asset Manager can be configured to upload images to a server or cloud storage). Another logical next step is setting up role-based restrictions or templates: you can create predefined templates that users start from, or lock certain components so they can't be edited (GrapesJS allows marking components as read-only).

Lastly, keep an eye on the GrapesJS project’s updates. As of 2025, the project is active with new features (e.g., recent support for newer frameworks, improved performance, and possibly official integrations). The community plugins are growing too. By integrating GrapesJS with Vue, you’ve combined a powerful UI framework with a powerful editor – giving you the best of both worlds for building modern, no-code/low-code content editors.


Good luck, and happy building! Your Vue 3 + GrapesJS landing page builder is ready to rock.

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 →