Embed a Svelte Drag and Drop Builder with BuilderJS
BuilderJS is a framework-agnostic, vanilla-JS drag-and-drop editor you embed in your own app, for HTML email and landing pages alike. There is no Svelte component to install and no npm package to import: you load the engine as a global browser script, then mount it from inside Svelte's onMount hook. This page shows the exact, copy-pasteable pattern. You add a stylesheet and a script tag so the browser attaches window.Builder, render three container divs in your component markup, and call new Builder with CSS selector strings pointing at those divs. Loading content is one call, builder.load() with four positional arguments, and saving is one call, builder.getData(), which you POST to a route you own. BuilderJS is the editor; it does not send email or host pages, so you bring your own sending and hosting. It ships under a one-time CodeCanyon license (Regular $52 / Extended $99): you own the source and self-host, with no SaaS, monthly fee, or per-seat metering.
Mount it
<script>
import { onMount } from 'svelte';
// window.Builder comes from the global builder.js script (there is no import).
onMount(() => {
const builder = new window.Builder({
mainContainer: '#bjs-canvas', // required, a CSS selector string
widgetsContainer: '#bjs-widgets', // optional
settingsContainer: '#bjs-settings' // optional
});
// load() takes 4 positional args, in this exact order:
builder.load(themeJson, themeTemplatesJson, themeConfigData, mediaUrl);
// Persist: POST the canvas JSON to a route you own.
window.save = () => fetch('/api/builder/save', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(builder.getData())
});
// No destroy(): on unmount Svelte discards these container divs.
});
</script>
<div id="bjs-widgets"></div>
<div id="bjs-canvas"></div>
<div id="bjs-settings"></div>
Why there is no import: a global window.Builder script
BuilderJS ships as a self-hosted browser bundle, not an ES module. Reference two files in your page head: a stylesheet link to /builderjs/builder.css and a script tag for /builderjs/builder.js. Loading the script attaches a global constructor at window.Builder. Because of this, you never write an import statement in your Svelte file: there is no named export to import, and trying to will fail. Your component reads the constructor straight off window instead. The same global works in Svelte, plain HTML, or any other host with zero build-tool coupling. Load the script before the component mounts (a plain tag in app.html or index.html is simplest), then reference window.Builder inside onMount.
Client-side DOM mount inside onMount
The editor is a DOM-mount bundle and must run in the browser, never during server-side rendering. In Svelte the correct hook is onMount, which only fires client-side after the component's DOM exists. Render three container nodes in your markup: a div with id bjs-widgets, a div with id bjs-canvas, and a div with id bjs-settings. Then in onMount call new Builder. The constructor options are CSS selector strings, not DOM nodes or Svelte refs: mainContainer is required and points at your canvas, while widgetsContainer and settingsContainer are optional sidebars. The engine resolves each selector with document.querySelector and manages that subtree itself, so it does not fight Svelte's render cycle. In SvelteKit this code must run only in the browser, and onMount guarantees that since it never runs during SSR.
Persistence: getData() to save, load() to restore
Saving the canvas is a single call: builder.getData() returns a plain serializable object describing the whole design. POST it to a route you own; BuilderJS does not store anything for you. To restore a saved design, or to seed a theme on first paint, call builder.load() with exactly four positional arguments in this order: themeJson, themeTemplatesJson, themeConfigData, and mediaUrl. The first argument is your saved getData() output (or a starter theme JSON); the others are the theme templates, its config data, and the media base URL for resolving relative asset paths. You control the round-trip, so you decide where the JSON lives: no hosted persistence, no phone-home, no per-document fee.
No destroy(): handling unmount and Strict Mode
BuilderJS has no destroy(), teardown(), or dispose() method, so never call builder.destroy(). On a real Svelte unmount the framework discards the component's DOM, including the canvas, widgets, and settings containers, and the editor goes with it; there is nothing to tear down by hand. The one thing to guard against is mounting twice. onMount runs once per real mount, so Svelte does not have React 18 Strict Mode's dev double-invoke; if you ever share mount logic with a framework that does double-invoke in development, set a boolean ref flag and instantiate exactly once. In plain Svelte, onMount alone is sufficient.
Frequently asked questions
-
How do I import BuilderJS into a Svelte component?
You do not import it. BuilderJS is a global browser script, not an npm package, and has no named export, so an import statement is wrong and will fail. Add the builder.css stylesheet link and the builder.js script tag to your page, which attaches window.Builder, then use that global inside onMount.
-
Does BuilderJS work with SvelteKit server-side rendering?
The editor mounts client-side only; it is a DOM-mount bundle and is never SSR'd. In SvelteKit, instantiate it inside onMount, which only runs in the browser after the DOM exists. The component can live in a SvelteKit route; just keep the new Builder call within onMount.
-
What containers does BuilderJS need?
Three nodes: a div with id bjs-widgets, a div with id bjs-canvas, and a div with id bjs-settings. Pass them to the constructor as CSS selector strings: mainContainer (the canvas) is required; widgetsContainer and settingsContainer are optional. They are selector strings, not DOM nodes or refs.
-
How do I save and restore what the user builds?
Call builder.getData() to get a serializable JSON object and POST it to your own backend route. To restore it, call builder.load() with the four positional arguments themeJson, themeTemplatesJson, themeConfigData, and mediaUrl in that order, passing your saved JSON as the first argument. BuilderJS does not host or store anything for you.
-
Should I call builder.destroy() when the component unmounts?
No, there is no destroy(), teardown(), or dispose() method. On real unmount Svelte discards the container DOM and the editor with it, so there is nothing to tear down. Just make sure you instantiate exactly once; onMount already runs a single time per mount.