An embeddable builder for LMS and course platforms — own the editor, not rent it
If you are building your own LMS, course platform, or membership product — not running courses on top of WordPress — you eventually hit the same fork in the road: how do creators design their sales pages, lay out lessons, and write drip and reminder emails? Stitching in Elementor or Divi means renting someone else's plugin, living inside their WordPress assumptions, and explaining their UI to your users as if it were yours. BuilderJS offers the other path: a framework-agnostic, vanilla-JavaScript drag-and-drop editor you license once, self-host, and embed directly inside your platform. One canvas builds two things from a single JSON model — web/landing HTML for course sales pages and lesson layouts, and Outlook-safe HTML for drip and reminder email templates. You extend it for your domain (quiz blocks, video embeds, progress widgets) by registering custom Elements through the public Builder.registerElement API, without forking the source. The design your creators make is plain JSON you store next to the course or module record in your own database. Honest scope up front: this is for teams shipping their own product and willing to self-host. If your platform is happy on the Elementor/Divi/WordPress path, that path is fine — BuilderJS is for when you want to own the editor instead. It is the editor only: it builds the markup; your platform still owns sending the emails, hosting the pages, and the LMS itself.
Mount it
// LMS: mount one editor that builds course pages AND reminder emails,
// extended with your own course block. builder.js attaches window.Builder
// (no npm package). Render the three containers, mount client-side.
// 1) Register a course-specific ELEMENT ONCE, before mounting — no fork.
// registerElement adds Elements only; the element's getControls()
// builds its property panel by subclassing the core Control classes.
window.Builder.registerElement('QuizBlockElement', QuizBlockElement);
// (Custom Widgets -> builder.widgetsBox.addWidget() in the load() callback;
// custom Controls -> subclass + instantiate inside getControls().
// There is no registerWidget/registerControl.)
// 2) Mount the editor in your LMS screen.
const builder = new window.Builder({
mainContainer: '#bjs-canvas', // REQUIRED — CSS selector string
widgetsContainer: '#bjs-widgets', // optional
settingsContainer: '#bjs-settings', // optional
});
// 3) Seed from the lesson/course record's saved JSON (4 positional args).
builder.load(lesson.themeJson, themeTemplatesJson, themeConfigData, mediaUrl);
// 4) Persist the design next to the course/module record in YOUR database.
// Same getData() JSON whether it renders as a page or an email later.
const json = builder.getData();
await fetch('/api/lessons/' + lesson.id + '/design', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(json),
});
// No destroy() exists; your framework discards the DOM on unmount.
One canvas, two outputs: course pages and reminder emails
Course platforms need two kinds of design surface, and most teams end up bolting on two separate tools. BuilderJS collapses them into one editor. The same drag-and-drop canvas produces modern-CSS HTML for the web side — course sales/landing pages, module overview pages, lesson layouts — and Outlook-safe HTML for the email side: welcome sequences, drip lessons, and the 'you haven't logged in for 3 days' reminder. The email path runs through the EmailExportPipeline, which converts flexbox/grid layouts to nested tables, rasterizes SVG to PNG, adds MSO conditional comments and ghost tables for Outlook, applies @media (max-width:600px) mobile stacking, makes images fluid, and inlines the CSS. The package ships 30+ templates and 200+ samples that were Litmus-tested across 22 email clients, so your creators' lesson reminders actually render in real inboxes. The honest boundary: BuilderJS builds the markup for both. It does not send the drip email and it does not host the course page — your platform's delivery and hosting layer does that, the same as it already does.
Extend it for your domain with registerElement — no fork
A generic page builder doesn't know what a quiz, a video lesson, or a course-progress bar is. BuilderJS lets you teach it without forking the library. Use the public Builder.registerElement API to add custom element types that sit in the palette alongside the built-in 42 element types — a quiz block, an embedded video player, a 'mark lesson complete' button, a curriculum/module list. Each custom element brings its own property panel by overriding getControls() and subclassing the same control system the core uses (59 property controls), so creators configure your quiz block the way they configure a heading. Because these element classes live in your own codebase against a stable public API rather than as patches inside the source, they survive updates instead of breaking on every version. That is the difference between owning an editor you can shape to a course platform and renting a plugin you have to bend your product around. (To be precise about the extension surface: custom Elements register through this static registerElement API; custom Widgets are added in your load() callback via builder.widgetsBox.addWidget(); and custom Controls are subclassed inline inside an element's getControls() — there is no separate registerControl or registerWidget call.)
JSON-first: store the design next to the course record
Every design in BuilderJS — page or email — is a plain JSON tree. You read it with getData() and restore it losslessly with load(), so save → reload → keep editing produces no drift. For an LMS that data model is the whole point: a lesson's layout JSON lives in the same row (or the same document) as the lesson; a course's sales-page JSON sits next to the course; a drip template's JSON belongs to the sequence step. Your backend owns persistence end to end — MySQL, Postgres, SQLite, a file, or S3 — there is no proprietary store you get evicted from and no usage meter counting designs. Because the design is text, you can version templates in git, diff a curriculum-page change in code review, and write a migration that rewrites a property across thousands of saved lessons in CI. The rendered HTML is a derivative you regenerate on demand; the JSON is the canonical artifact you keep.
Multi-tenant by construction, owned by a one-time license
A course platform serves many creators, and BuilderJS is multi-tenant capable through closure-captured configuration — you can run an isolated editor instance per creator or per workspace from the same self-hosted bundle. The honest caveat, stated plainly: BuilderJS supplies the embeddable editor; your platform still owns tenant isolation, seats, and billing. On cost, this is the wedge against renting an editor. BuilderJS is sold under a one-time CodeCanyon license (item 27146783), Regular or Extended, paid once — you get the full source, self-host builder.js and builder.css from your own origin, and embed it in your platform with no SaaS account, no monthly fee, no per-seat charge, no usage metering, and no phone-home. If your creators are charged end users, Extended is the tier to choose. The honest boundary on ownership: the code is yours to keep and run perpetually, but author support and version updates are time-boxed (roughly 6–12 months under CodeCanyon's standard window), after which you self-maintain the source you own. We never call support or updates 'lifetime.'
When BuilderJS fits — and when it honestly doesn't
Be honest with yourself about which platform you are. BuilderJS fits when you are building your own LMS, course, or membership product — non-WordPress or a custom stack — and you want one ownable canvas for sales pages, lesson layouts, and reminder emails, embedded so it looks like your product and extended with your own quiz/video blocks. It mounts unchanged in React, Vue, Svelte, Angular, Next.js, Laravel, or plain HTML, so it survives your stack choices. It does not fit if your platform lives on WordPress and your creators are happy designing in Elementor or Divi — that ecosystem is mature and there is no reason to rebuild it. BuilderJS is also the editor, not the platform: it does not run the LMS, enrol students, gate content, send the email, or host the page. It builds the page and email markup; you bring (or already have) the delivery, hosting, auth, and course logic. If that division of labor matches how you think about your product, the one-time-license, own-the-editor model is the reason to choose it. You can try the getData()/load() round-trip on the live demo at builder.emotsy.com before you commit, then read /one-time-license/ for the ownership model and /embed/react/ for the framework-specific mount guide.
Frequently asked questions
-
Is BuilderJS a WordPress plugin like Elementor or Divi for my course site?
No. BuilderJS does not ship as a WordPress plugin — that is what Elementor is. BuilderJS is a framework-agnostic JavaScript library you embed inside your own LMS or course platform (React, Vue, Svelte, Angular, Next.js, Laravel, or plain HTML) and self-host. It is for teams building their own product who want to own the editor rather than rent a plugin. If your platform runs on WordPress and your creators are happy in Elementor or Divi, stay on that path; BuilderJS is for the other case.
-
Can it build both course sales pages and the drip/reminder emails?
Yes — that is the headline. One drag-and-drop canvas produces modern-CSS HTML for course sales pages, module pages, and lesson layouts, and Outlook-safe HTML for drip and reminder emails through the EmailExportPipeline (flexbox-to-table conversion, SVG-to-PNG, MSO conditionals, @media mobile-stacking, CSS inlining), with templates Litmus-tested across 22 clients. The boundary: BuilderJS builds the markup. It does not send the email or host the page — your platform's delivery and hosting layer does that.
-
How do I add course-specific blocks like quizzes or video lessons?
Use the public Builder.registerElement API to register custom element types — a quiz block, a video-lesson embed, a progress bar, a curriculum list — that appear in the palette next to the built-in 42 element types and expose their own property panels by overriding getControls() against the 59-control system. Custom Widgets are added separately in your load() callback via builder.widgetsBox.addWidget(), and custom Controls are subclassed inline inside an element's getControls(); there is no registerWidget or registerControl. Crucially, these classes live in your own codebase against a stable public API, so you extend the editor for your domain without forking the source, and your custom blocks survive updates.
-
Where does a lesson or course design get stored?
In your own database, next to the record it belongs to. Every design is a plain JSON tree: call getData() to serialize it and load() to restore it losslessly. You store a lesson's layout JSON with the lesson, a sales-page JSON with the course, and a drip-template JSON with the sequence step — in MySQL, Postgres, SQLite, a file, or S3. There is no proprietary store and no usage meter; because it is JSON you can also version designs in git and run migrations across thousands of saved lessons.
-
How is it licensed for a multi-tenant course platform?
BuilderJS is a one-time CodeCanyon license (item 27146783): Regular or Extended, paid once. It is genuinely one-time — no SaaS, no monthly fee, no per-seat charge, no usage metering, no phone-home — so the editor's cost does not scale with how many creators or students you have. It is multi-tenant capable via closure-captured configuration, but your platform owns tenant isolation, seats, and billing. If you charge your creators as end users, choose Extended, and review the CodeCanyon terms for your distribution model.