Frontend Development 14 min read

How to Build a Drag‑Drop Site Builder with Vue.js: From Data Model to Rendering

This article explains how to design and implement a Vue.js‑based website builder that lets users create multi‑page sites by dragging modules, covering requirement analysis, JSON data modeling, dynamic component rendering, state management with Vuex, and drag‑and‑drop integration.

Baixing.com Technical Team
Baixing.com Technical Team
Baixing.com Technical Team
How to Build a Drag‑Drop Site Builder with Vue.js: From Data Model to Rendering

As a frontend engineer, you may have built many websites and applications. This article classifies sites into "presentation" and "operation" types, and describes the design and implementation of an "operational" site that serves as a "site‑builder for websites" using Vue.js.

Requirements

Typical website‑builder features include:

Providing templates

Theme colors

Rich functional modules such as text, carousel, gallery, etc.

Draggable modules with configurable options

Support for creating multiple pages

Page layout with sections/columns

Responsive design for handheld devices

Requirement Analysis

Key concepts extracted from the requirements are "template", "theme color", "module", "page", and "section". Their meanings are clarified to guide the design.

Page : A website consists of one or more pages.

Section : A page is divided into functional areas (e.g., company intro, case studies, contact). We refer to these as "sections".

Module : Each section contains one or more components (modules) that achieve a specific purpose. A module corresponds to a Vue component.

Template : Defines the layout of a page, including the number of sections and their horizontal proportions.

Theme Color : Combined with a template to form the site’s visual style.

Website Data Representation

The site is represented as a tree‑structured JSON object, where a site contains pages, pages contain sections, and sections contain modules. This hierarchical model can be visualized as:

Example JSON representation:

<code>{
  "id": 1,
  "name": "xxx公司",
  "type": "site",
  "children": [{
    "type": "page",
    "name": "首页",
    "children": [{
      "type": "section",
      "name": "公司简介",
      "children": [{
        "type": "paragraph"
      }, {
        "type": "carousel"
      }]
    }]
  }]
}
</code>

Modules are leaf nodes; each module has

content

(the actual data) and

config

(settings such as animation or autoplay for a carousel).

Theme color is stored in

site.config.themeColor

. Responsive layout can be achieved by adding Bootstrap grid classes to a section’s

config

, e.g.,

class="col-xs-12 col-sm-6 col-md-3"

.

Thus, a complete site can be expressed as a tree‑structured JSON containing all pages, sections, modules, and their configurations.

From Data to Site

Rendering the site involves traversing the JSON tree and rendering each node with a corresponding Vue component.

Write a component for each node type that receives

node

and

themeColor

as props.

Recursively render the tree by rendering a node and then its children.

Example of a simple paragraph component (Paragraph.vue):

<code><!-- Paragraph.vue -->
<template>
  <div>
    <h1 :style="{color: themeColor}">{{node.content.title}}</h1>
    <small v-if="node.config.showSubTitle">{{node.content.subTitle}}</small>
    <p>{{node.content.detail}}</p>
  </div>
</template>
<script>
export default {
  name: 'paragraph',
  props: ['node', 'themeColor']
}
</script>
</code>

The renderer component uses Vue’s dynamic

component

to select the appropriate component based on

node.type

:

<code><!-- render.vue -->
<template>
  <component :is="node.type" :node="node" :theme="themeColor">
    <render v-for="child in node.children" :key="child.id" :node="child" :theme="themeColor" />
  </component>
</template>
<script>
// Import all node components
import Page from './Page.vue'
import Section from './Section.vue'
import Paragraph from './Paragraph.vue'
export default {
  name: 'render',
  props: ['node', 'themeColor'],
  components: { Page, Section, Paragraph }
}
</script>
</code>

If Vue’s built‑in dynamic component is unavailable, the same effect can be achieved with

createElement

(see the linked gist).

Editing and Saving

Creating a site involves selecting a template, adjusting the theme color, dragging modules, editing module content/configuration, and finally persisting the JSON tree.

Select a template → initializes the tree with default colors and modules.

Change theme color → updates

site.config.themeColor

.

Drag a module into a section → pushes a new node into

section.children

.

Reorder modules → updates the index within

section.children

.

Edit module content/config → modifies the node’s

content

and

config

.

Save site → stores the JSON tree in a database.

State management is handled with Vuex. A simple store definition:

<code>// store.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const store = new Vuex.Store({
  state: { site: {} },
  mutations: {
    changeThemeColor () {},
    addModule () {},
    sortModule () {},
    removeModule () {},
    updateModule () {}
  },
  actions: {
    getSite () {},
    saveSite () {}
  }
})
</code>

To automatically persist changes, a Vuex plugin watches the

site

state and dispatches

saveSite

on any modification:

<code>// store.js
const autoSave = (store) => {
  store.watch(
    state => state.site,
    (newV, oldV) => {
      store.dispatch('saveSite')
    },
    { deep: true }
  )
}
const store = new Vuex.Store({
  state: { site: {} },
  plugins: [autoSave]
})
</code>

For editing, each module gets a corresponding edit component that wraps the display component, allowing inline or modal editing. The edit component adds an

edit-

prefix to the node type, enabling the renderer to switch between read‑only and editable modes.

Drag‑and‑drop is implemented with

Vue.Draggable

(a Vue wrapper for Sortable.js). The component listens to

add

and

sort

events and calls the appropriate Vuex mutations.

Conclusion

The article walks through requirement analysis, data modeling, and Vue.js implementation for a drag‑and‑drop site‑builder, emphasizing how Vue.js handles data‑to‑view rendering and reactive updates, allowing developers to focus on business logic.

frontend architectureVue.jsdynamic componentsdrag and dropVuexJSON data modelsite builder
Baixing.com Technical Team
Written by

Baixing.com Technical Team

A collection of the Baixing.com tech team's insights and learnings, featuring one weekly technical article worth following.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.