← Back to Posts

Experimenting with HTML to JSON

October 7, 2024

With the recent Wordpress drama going on, I decided to see for myself how difficult (or easy) it would be to replicate Wordpress' "Drag-n-Drop" page builder that gives it an edge over competitors.

Finding the right libraries

For this excursion, I started with Vue and keeping it simple (read "not creating my own library"), I searched NPM for a HTML to JSON library of which there were a few. However, I eventually settled on html-to-json-parser.

A simple library, it took the following HTML and translated it to JSON, and could do vice versa translations too!

<div class="container">
	<ul>
		<li>Hello <strong>World</strong></li>
	</ul>
</div>
{
  "type": "div",
  "attributes": {
    "class": "container"
  },
  "content": [
    {
      "type": "ul",
      "content": [
        {
          "type": "li",
          "content": [
            "Hello ",
            {
              "type": "strong",
              "content": [
                "World"
              ]
            }
          ]
        }
      ]
    }
  ]
}

The next step was to figure out how to do Drag and Drop for Vue. Unfortunately, there aren't many Drag and drop libraries built for Vue that have remained up to date and so I was somewhat forced to use Vue Draggable Plus, an upgrade from Vue Draggable with some extra features.

Armed with these two libraries, and a few free evenings, I cobbled together a rather rudimentary Page Builder capable of dragging and dropping, and styling (using Tailwind) of HTML components onto a "Page Viewer".

The "end" product

A screenshot early into development showing how Vue Draggable Plus renders nested components. On the left is a list of draggable components, each in the JSON format that json-to-html-parser library produces. This way when they're added to the "Page Viewer" component (and the v-model), the library can render JSON back to HTML

Additionally, as each JSON object has a "content" array, I built the PageViewer component so that items can be nested several layers deep inside parent components.

Below is a screenshot of the Attributes sidebar that enables an element to have classes added to it, further development here will support individual element types, i.e an anchor tag having a "href" field and so on.

This sidebar works by listening to a click event on the element and setting it as "selectedElement" in a Pinia store to be shown on the sidebar.

It's by no means anywhere finished, but the code is open-source and I do plan on developing this further once I finish my excursion within an excursion; building an API to store the data from this Page Builder.

Both projects have presented lots of challenges so far which means they're absolutely worth figuring out, I'll write a follow up article in a few weeks once the API is bit more fleshed out

🎨 https://github.com/dmdboi/vue-page-builder
https://github.com/dmdboi/vue-builder-api

Notes

  • Rendering of recursive items can be a nightmare (see my tweet)