A visual page editor for your headless CMS
Headless CMSes like Contentful, Sanity, and Strapi nailed structured content and API-first delivery, but they leave one gap: editors still type into long-text fields or wrestle with rich-text when they want to compose an actual page. BuilderJS fills that gap without breaking the headless model. It is a ~140KB vanilla-JS drag-drop editor library you embed in any DOM node — React, Vue, Svelte, Angular, Next.js, Laravel, or plain HTML. Your editors drag from 42 element types; getData() hands you back lossless JSON, and load() restores it exactly. The fit is precise: that JSON is just data, so you store it in a content field and render it in your own frontend. BuilderJS imposes no schema, runs no server, and hosts nothing — your headless CMS stays the system of record and your frontend stays in control. One-time CodeCanyon license (item 27146783), self-hosted, no monthly fee, no per-seat charge, no usage metering. See it live at builder.emotsy.com.
JSON output that drops straight into a content field
The reason BuilderJS fits a headless CMS is the data shape. Call getData() and you get a structured JSON document — not baked HTML, not a proprietary blob locked to a vendor. You store that JSON wherever your CMS keeps a field: a JSON field in Contentful, a custom block in Sanity's Portable Text, a JSON column in Strapi, or a rich-text/object field in any API-first store. The round-trip is lossless: feed the same JSON back through load() and the canvas comes back pixel-identical for the next edit. Because the canonical form is portable JSON, you can version it, diff it, migrate it, and query it like any other structured content. The headless promise — content as data, decoupled from presentation — holds, because BuilderJS treats the design as exactly that.
It imposes no schema and no hosting
This is the honest strength worth scoping to. BuilderJS is the editing surface only. It does not define content models, run a delivery API, host pages, serve a CDN, or publish to a URL — your headless CMS already owns all of that, and BuilderJS does not duplicate or fight it. There is no second backend to operate and no opinion forced onto your content schema; you decide what the JSON field is called and how it relates to the rest of your model. That clean separation is precisely why it pairs with an API-first stack instead of competing with it: the CMS handles modeling, storage, and delivery; BuilderJS handles the visual authoring step that headless tools deliberately left open. You bring the persistence and the hosting; BuilderJS builds the markup.
Render the design in your own frontend
Headless means you own the frontend, and BuilderJS respects that. After an editor finishes, your app reads the JSON field from the CMS API and turns it into output. Two paths work cleanly: load the JSON into a Builder instance and call getHtml() to produce markup, or — because the format is plain JSON — render it however your frontend already renders structured content. The same JSON can target more than one output: standard responsive page HTML for the web, or Outlook-safe email HTML through the EmailExportPipeline (flexbox collapsed to tables, SVG rasterized to PNG, @media mobile-stacking, MSO conditionals, CSS inlined). So the visual block your editors composed in the CMS can drive both a landing page in your Next.js or Nuxt frontend and a campaign email your app sends through its own ESP.
Embed once, in the stack your frontend already runs
BuilderJS ships as a DOM-mount bundle — dist/builder.js plus dist/builder.css — not a published npm React or Vue component, so it mounts the same way regardless of which frontend your headless setup uses. Mount it client-side after the DOM exists: from useEffect in React, onMounted in Vue, ngAfterViewInit in Angular, onMount in Svelte, or a single script tag in plain HTML. In Next.js, load it via a dynamic import with ssr:false inside a 'use client' component, since the editor is browser-only. Extend it for your content model without forking: register custom Elements via Builder.registerElement, and add Widgets and Controls through their own extension points, so vendor updates never collide with your blocks. It bundles themes and i18n across 20+ locales with RTL, and reference PHP backends for save/upload/auth (MySQL/SQLite/S3) you copy, own, and port to Node, Go, or Python.
How it differs from a hosted visual headless CMS
Some headless products bolt a visual editor onto a managed content pipeline — Builder.io, for example, is a SaaS visual headless CMS that stores your content and delivers it from the edge via SDKs and APIs, billed per user per month with metered AI Agent Credits (free tier capped at 5 users; builder.io/m/pricing, as of June 2026). That bundles editor, storage, and delivery into one rented stack. BuilderJS is deliberately narrower: it is only the editing surface, and it assumes you already have a headless CMS you like. You keep Contentful, Sanity, or Strapi for modeling and delivery; you add BuilderJS for the visual authoring step; you self-host the editor under a one-time license with no metering. Pick the hosted CMS if you want the whole pipeline rented. Pick BuilderJS if your content backend is set and you just need the editor inside it.
Frequently asked questions
-
How does BuilderJS store designs in a headless CMS like Contentful or Sanity?
As JSON. Call getData() to get a structured JSON document and write it to a content field — a JSON field in Contentful, a custom block in Sanity, a JSON column in Strapi, or any object field your API-first store offers. To edit again, read that field back and pass it to load(); the round-trip is lossless. BuilderJS treats the design as portable data, which is exactly what a headless CMS expects.
-
Does BuilderJS replace my headless CMS?
No, and it is not meant to. BuilderJS is only the visual editing surface — it does not model content, run a delivery API, store data, or host pages. Your headless CMS stays the system of record for modeling, storage, and delivery. BuilderJS adds the drag-drop authoring step that API-first tools deliberately leave open, then hands the design back as JSON for your CMS to store.
-
Does it impose a content schema on my CMS?
No. BuilderJS imposes no schema and runs no backend of its own. You decide what the JSON field is called and how it fits your existing content model. The editor only produces and consumes JSON; everything about modeling, validation, and storage stays under your control inside your headless CMS.
-
How do I render the stored design in my own frontend?
Read the JSON field from your CMS API, then render it in your frontend. You can load the JSON into a Builder instance and call getHtml() to produce markup — standard responsive page HTML, or Outlook-safe email HTML through the EmailExportPipeline — or render the JSON however your frontend already handles structured content. The frontend stays entirely yours; BuilderJS just supplies the design data and markup.
-
What does BuilderJS cost, and is it a subscription?
It is a one-time CodeCanyon license (item 27146783) — Regular at $52 and Extended at $99. You own the source and self-host it: no SaaS, no monthly fee, no per-seat charge, no usage metering, and no phone-home. Author support is time-boxed (roughly 6-12 months). Any recurring cost in your stack comes from the headless CMS you already run, not from the editor.