Embed a JavaScript HTML Builder in Your App — One Script Tag, No Framework
BuilderJS is a self-contained, framework-agnostic drag-and-drop editor you embed in your own app — no React, Vue, or build pipeline required. The whole library ships as two static files (dist/builder.js + dist/builder.css, ~140KB), so you add one script tag and one stylesheet tag, give it an empty DOM node to mount into, and you have a working visual editor for both HTML email and web/landing pages. It is the editor you embed, not a platform: it produces the markup, and your app keeps doing the persistence — saving the result, and handing it to your own ESP for sending or your own host for publishing. State is plain JSON — getData() gives you a lossless tree you POST to your own backend, and load() paints it back exactly. BuilderJS bundles reference PHP backends (save, upload, auth on MySQL/SQLite/S3) you copy and own, but the contract is just a JSON POST, so porting persistence to Node, Go, or Python is straightforward. Live demo: builder.emotsy.com.
Mount it
<!-- index.html: two tags + an empty mount node -->
<link rel="stylesheet" href="/dist/builder.css">
<div id="canvas"></div><div id="widgets"></div><div id="settings"></div>
<script src="/dist/builder.js"></script>
<script>
// Client-side only: mount once the DOM is ready.
document.addEventListener('DOMContentLoaded', async () => {
const builder = new Builder({
mainContainer: '#canvas',
widgetsContainer: '#widgets',
settingsContainer: '#settings',
});
// Restore saved JSON (or your seed sample) into the editor.
// THEME_TEMPLATES / THEME_CONFIG_DATA / MEDIA_URL are emitted server-side.
const saved = await fetch('/api/page').then(r => r.json());
builder.load(saved, THEME_TEMPLATES, THEME_CONFIG_DATA, MEDIA_URL);
// Persist: getData() => lossless JSON for round-trip load().
document.querySelector('#save').addEventListener('click', () =>
fetch('/api/page', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ html: builder.getHtml(), data: builder.getData() }),
})
);
});
</script>
Two tags and an empty div — that's the whole install
There is no npm install, no bundler config, and no transpile step for the plain-HTML path. Copy dist/builder.js and dist/builder.css into your static assets, reference them with a normal <script> and <link> tag, and add an empty container element where the editor should appear. On the client you call new Builder({ mainContainer, widgetsContainer, settingsContainer }), pointing each option at a CSS selector for an element already in the DOM. Because BuilderJS mounts into any DOM node rather than owning the page, it drops cleanly into a server-rendered Laravel/Rails/Django page, a static HTML file, or a slot inside an existing dashboard. The library is closure-captured per instance, so you can mount several editors on one page — for example a separate header, body, and footer builder — each with its own widgets palette and loaded theme.
JSON in, JSON out — your backend owns persistence
Saving is deliberately boring: call builder.getData() to get the page tree as JSON and builder.getHtml() to get the rendered markup, then POST both to whatever route you already have. On the next load you fetch that JSON back and hand it to builder.load() — every block, format, and uploaded asset returns to where the user left off. The round-trip is lossless, so nothing degrades when you store and reload. BuilderJS includes reference PHP endpoints (save.php, upload handlers) wired to MySQL, SQLite, or S3 that you copy into your project and own outright. Since the wire format is just a JSON body plus an HTML string, re-implementing those endpoints in Express, FastAPI, or a Go handler is a small job — the editor does not care what language answers the fetch.
Builds both email HTML and web pages from one editor
The same embedded editor produces two distinct outputs. For web/landing pages it emits standard responsive HTML. For email it runs an export pipeline that converts flexbox layouts to tables, rasterizes SVG to PNG, applies @media mobile-stacking, and injects MSO conditional comments and inlined CSS so the result survives Outlook and the long tail of email clients. The library ships 30+ templates and 200+ samples that have been Litmus-tested across 22 clients, plus 42 element types, 59 controls, and 31 overlays. You choose which output you want at export time; BuilderJS generates the template or markup, and you bring your own ESP or host to actually send the email or publish the page.
Extend it without forking — and own the source
You register custom Elements, Widgets, and Controls through Builder.registerElement rather than patching the library, so your additions survive updates. It supports themes, i18n across 20+ locales with RTL, and vendor-neutral AI hooks you wire to whatever provider you prefer. It is multi-tenant capable — instances are closure-captured — but your app owns tenant isolation, seats, and billing; the editor has no opinion on accounts. Licensing is a one-time CodeCanyon purchase (item 27146783, Regular $52 / Extended $99): you get the source, self-host it, and pay no monthly fee, no per-seat charge, no usage metering, and there is no phone-home. Compare that with usage-metered embeddable SDKs: the GrapesJS Studio SDK, for instance, lists session-based tiers from a free 1,000-sessions/month plan up to Startup at $200/month (20,000 sessions) and Business at $2,000/month (50,000 sessions), with overage billed per 1,000 sessions above each tier's allowance (as of June 2026, https://grapesjs.com/sdk/pricing).
Frequently asked questions
-
Do I need React, Vue, or a build step to embed it?
No. The plain-HTML path is just two static files — dist/builder.js and dist/builder.css (~140KB total). You add a <script> tag, a <link> tag, and an empty container element, then call new Builder({...}) on the client. There is no npm package, bundler, or transpile step required. It mounts into any DOM node, so it works the same in a static HTML file or a server-rendered Laravel/Django/Rails page.
-
How do I save and reload what the user built?
Call builder.getData() to get the page as lossless JSON and builder.getHtml() for the rendered markup, then POST both to your own backend route. On the next page load, fetch that JSON back and pass it to builder.load() to restore the exact state. BuilderJS does not store anything for you — your app owns persistence — but the contract is a plain JSON POST, so it fits any database or storage you already use.
-
Can I use the reference backend in Node, Go, or Python instead of PHP?
Yes. BuilderJS ships reference PHP backends (save, upload, auth on MySQL/SQLite/S3) that you copy and own. They are a convenience, not a requirement — the editor only sends a JSON body and an HTML string over fetch. Re-implementing the same save and upload endpoints in Express, FastAPI, or a Go handler is straightforward because the wire format is identical regardless of server language.
-
Does BuilderJS send the emails or host the pages it builds?
No. BuilderJS is the editor you embed — it builds the email template or page markup, and that is where its job ends. It does not send email, host or publish pages, run funnels, or do CRM and automation. You bring your own ESP to send and your own host to publish. For email specifically, its export pipeline produces Outlook-safe HTML (table layout, MSO conditionals, inlined CSS) that you then hand to your sending platform.
-
What does it cost, and are there recurring fees?
BuilderJS is a one-time CodeCanyon license (item 27146783): Regular $52 / Extended $99. You own the source and self-host it. There is no SaaS subscription, no monthly fee, no per-seat charge, no usage metering, and no phone-home. By comparison, some embeddable builder SDKs bill by usage — the GrapesJS Studio SDK lists tiers from a free 1,000-sessions/month plan up to Startup at $200/month and Business at $2,000/month, with per-1,000-session overage above each tier (as of June 2026, https://grapesjs.com/sdk/pricing).