Launch the spring campaign
Click this text and type over it.
PageKit — the self-hosted GrapesJS site builder, sold as source. Get early access
Build a visual, drag-and-drop landing page builder with reusable sections, responsive editing, custom components, forms, SEO controls and your own publishing workflow.
Blocks
Layers
Styles
Start from a blank canvas or a template, drag sections into place, customize styles and content, and publish through your own application. Drag a block into the canvas below — or tap a block, then tap the canvas — to see the gesture your users will be making.
Sections
Drag a section in — or tap a section, then tap the canvas.
Click this text and type over it.
Short, scannable, benefit-first.
Three tiers, one recommended.
Styles
Accent
Three public GrapesJS builds, loaded only when you ask for one — nothing is fetched until you click.
Open in a new tabThe stock GrapesJS demo: block palette, canvas, style manager and layer tree. Everything visible here ships in the open-source core.
Loads a third-party demo in an iframe. Nothing is requested until you click.
A landing page builder looks like one feature and behaves like a dozen. Before a marketer can drag a hero section into place, someone has to have built a canvas, a component model, a drag-and-drop system, a style manager, responsive editing, undo, asset handling and a serialization format — and then keep all of it working.
Design the editor architecture first, ship the product second.
You implement and maintain
Every one of those is a subsystem with its own edge cases, and none of them is the thing your customers pay for.
Take the editing engine as given and customize it.
Already in the open-source core
You configure the panels, register your own sections, and spend your engineering budget on the product instead.
See the quickstartAdd the marketing-specific pieces without writing them.
Installed rather than built
Install, configure, customize, ship — and keep your team's code for the parts nobody else can build for you.
Browse pluginsGrapesJS gives you the visual editing foundation so your team can focus on the product around it.
Nineteen subsystems, tagged by who ends up owning each one. The point is not that the list is long — it is that only the last four are specific to your business.
GrapesJS covers the editing engine. Plugins cover the landing-page-specific surfaces. Everything in the last lane is where your product actually differs from everyone else's.
A landing page builder is a visual editor that lets users create campaign pages without hand-writing every HTML element and CSS rule.
It is not one feature. A landing page builder is an editor plus blocks plus templates plus forms plus SEO plus analytics plus experiments plus publishing — and each of those is a decision about who owns what.
In practice, a modern landing page builder is expected to provide all of the following:
What teams expect from one
The user manipulates the rendered page directly. A drag-and-drop landing page builder makes layout a gesture rather than a merge request.
Marketers think in heroes, pricing tables and FAQs. A landing page editor built around section blocks matches how campaigns are actually planned.
The output is markup you control and can render anywhere — not a proprietary format locked to a hosted runtime.
GrapesJS provides the visual editing layer. Your application controls the backend, data, authentication and publishing workflow.
The same visual editing engine underwrites very different products. What changes is the application around it — who logs in, what they are allowed to do, and where the page ends up.
Launch product and campaign pages quickly, without an engineering ticket per headline change.
Drag-and-drop mechanicsLet customers create pages directly inside your SaaS, under their own account and their own branding.
Building a SaaS builderCreate one reusable editing system and apply it across multiple client projects.
White-label buildersProvide a fully branded page creation experience where nothing on screen says GrapesJS.
White-label approachGive content teams visual editing on top of the content model you already have.
Headless CMS editingAllow the marketing team to create pages without engineering support, inside your own tooling.
Open-source approachShip and iterate campaign pages without first building a complete CMS.
The HTML outputEmbed visual page creation directly into another application your customers already use.
Embedding a builderYour application
Project data
GrapesJS
Authoring to live page
GrapesJS owns the editing surface. Your application owns identity, data and delivery. Keeping that line clean is what makes the builder replaceable, testable and yours.
These are the editing capabilities GrapesJS provides out of the box, and the ones its plugin API is designed to extend.
Build layouts visually. Components are dropped, reordered and nested on the canvas itself.
Register your own sections and components once; every user drags the same library.
Switch devices in the editor and write rules per breakpoint. The default device set is desktop, tablet, mobile landscape and mobile portrait.
Define component types with their own traits and behaviour so the editor matches your product's design system.
Manage images and other media through the asset manager, backed by whatever storage you already use.
Control typography, spacing, colours and layout through sector-grouped controls you can configure.
Start campaigns from reusable layouts instead of a blank canvas, via the template plugins in the catalogue.
getHtml() and getCss() return the markup the canvas rendered, so the published output stays yours to optimize.
Choose a template
Open a saved layout rather than an empty page. The fastest campaign is one that starts 70% finished.
Add sections
Drag hero, features, pricing, testimonials and FAQ blocks into the order the campaign needs.
Customize content
Edit copy in place on the canvas, swap images through the asset manager, set links.
Adjust responsive styles
Switch devices and fix the layout per breakpoint before anyone sees it on a phone.
Add forms
Drop in the capture form and point it at your own endpoint.
Preview
Render the page from project data on a preview route — the same renderer that serves the live page.
Publish
Write the HTML, CSS and SEO record, assign the slug, and serve it from your own infrastructure.
Create once. Reuse across campaigns.
GrapesJS core ships zero blocks. That is not an omission — it is the extension point. The block library you register is exactly what turns a generic visual editor into a purpose-built landing page builder, and it is the highest-leverage code you will write.
Headline, subhead and the primary call to action.
Two to four benefit columns with icons.
Customer or integration logos as social proof.
Quotes, names and avatars near the conversion point.
Tiered plans with one recommended column.
Feature table against the alternative the visitor is weighing.
Objection handling, in the visitor's own words.
The lead capture surface, posting to your API.
Single-field subscribe, for the visitor who is not ready yet.
The repeated ask, at the bottom of the page.
Navigation, legal and trust signals.
Install the core, mount it, and register the sections that make it a landing page builder. Everything else on this page is a decision layered on top of these lines.
Install
npm install grapesjsimport grapesjs from 'grapesjs';
import 'grapesjs/dist/css/grapes.min.css';
const editor = grapesjs.init({
container: '#editor',
height: '100vh',
// The core ships no blocks at all — the section library below is what
// turns a generic visual editor into a *landing page* builder.
blockManager: { appendTo: '#blocks' },
storageManager: false,
});
const SECTIONS = [
{ id: 'hero', label: 'Hero', html: '<section class="hero">…</section>' },
{ id: 'features', label: 'Features', html: '<section class="features">…</section>' },
{ id: 'logos', label: 'Logo cloud', html: '<section class="logos">…</section>' },
{ id: 'pricing', label: 'Pricing', html: '<section class="pricing">…</section>' },
{ id: 'faq', label: 'FAQ', html: '<section class="faq">…</section>' },
{ id: 'cta', label: 'Call to action', html: '<section class="cta">…</section>' },
];
SECTIONS.forEach(({ id, label, html }) => {
editor.Blocks.add(id, { label, category: 'Landing page', content: html });
});Each block is a label, a category and a chunk of markup. Give the markup a component type and it also gets traits, constraints and its own toolbar.
A template is a saved project a user opens instead of a blank canvas. The categories below are the layout kinds marketing teams ask for — the plugins underneath are what let your users save, manage and reopen them.
Layout kinds teams ask for
These are layout kinds, not catalogue listings: there is no template product type on GJS.Market. The template and page managers below are real plugins that give your builder somewhere to keep them.
A landing page without a capture surface is a brochure. The form component is an editor concern; everything after the submit button is yours.
Submission path
Route every submission through your own endpoint. That is where validation, rate limiting, spam handling, consent and CRM routing belong — and none of them are things a page editor should be deciding.
// The form component is editor-side. The submission is yours.
// Never let the published page post straight to a third party you do not
// control — route it through your own endpoint so you keep the lead.
app.post('/api/leads', async (req, res) => {
const parsed = leadSchema.safeParse(req.body); // validate server-side
if (!parsed.success) return res.status(422).json({ error: 'invalid' });
await rateLimit(req.ip); // forms get abused
const lead = await db.leads.create({
data: { ...parsed.data, pageSlug: req.body.pageSlug, utm: pickUtm(req.body) },
});
await crm.upsertContact(lead); // your CRM, your rules
res.json({ ok: true });
});Name, work email, message. The default sales path on a B2B campaign page.
Single field, low friction, for visitors who are interested but not ready.
Straight into the product, with the campaign parameters carried through.
Company size, role, budget — the fields sales needs before the first call.
A campaign page that a marketer cannot title, describe or canonicalize without a developer is not finished. GrapesJS edits the body of the document; the head belongs to your application — so the SEO record has to be part of your page model from the start.
Page SEO
<title>meta[name=description]link[rel=canonical]meta[property^=og:]og:imagemeta[name=robots]application/ld+jsonStored beside the project data, edited in the same screen, emitted by your renderer on publish.
Treat SEO metadata as a first-class field on the page record, not as something bolted on after launch. Marketers should be able to change a title without opening a pull request.
// SEO belongs to your application, not to the canvas.
// Keep the marketing metadata beside the project data, so a marketer can
// edit it in the same screen and your renderer can emit it on publish.
type LandingPage = {
slug: string;
project: unknown; // editor.getProjectData()
seo: {
title: string;
description: string;
canonical?: string;
ogImage?: string;
robots?: 'index,follow' | 'noindex,nofollow';
jsonLd?: Record<string, unknown>;
};
};
// On publish, your renderer — not GrapesJS — writes the <head>.
function renderHead(page: LandingPage) {
return [
`<title>${escapeHtml(page.seo.title)}</title>`,
`<meta name="description" content="${escapeHtml(page.seo.description)}">`,
page.seo.canonical && `<link rel="canonical" href="${page.seo.canonical}">`,
page.seo.robots && `<meta name="robots" content="${page.seo.robots}">`,
].filter(Boolean).join('\n');
}A landing page builder becomes much more valuable when teams can iterate on campaigns instead of treating pages as static assets. Once a page is data rather than a file, a variant is cheap: it is another project row pointing at the same slug.
Build
Duplicate the page in the editor and change one thing — the headline, the offer, the form length.
Publish
Both variants publish to the same slug, with weights your application decides.
Measure
Assign a visitor once, persist the assignment, and report it alongside your conversion events.
Improve
Promote the winner to the default and start the next test from it.
Assignment, weighting and significance are application logic. GrapesJS is not involved past the point where the two variants were authored — which is exactly why it can stay out of the serving path.
There is no A/B testing plugin in the GJS.Market catalogue, and GrapesJS has no experiment feature. This is a pattern to implement in your application, not a product to install — and a page builder is what makes it inexpensive.
// A/B testing is an application concern, not an editor feature.
// A landing page builder makes it cheap because a variant is just
// another project row pointing at the same slug.
const variants = await db.pageVariants.findMany({
where: { slug, status: 'running' },
});
// Assign once, persist the choice, and report it to your analytics.
const variant = pickWeighted(variants, hash(visitorId));
res.setHeader('Set-Cookie', `exp_${slug}=${variant.id}; Path=/; Max-Age=2592000`);
res.setHeader('Vary', 'Cookie'); // or the page will be cached for everyone
return renderPublishedPage(variant);You do not need to build an analytics platform inside your page builder. You need published pages to emit the events your existing stack already understands.
Fire on the interactions that matter — form submits, CTA clicks, scroll depth on long pages.
Tie a submission back to the page, the variant and the campaign that produced it.
The baseline every other number is a ratio of.
Preserve UTM values from the first click through to the CRM record.
Ship the container or tag your team already uses rather than inventing a reporting layer.
Use your own analytics infrastructure. The builder's job is to make sure every published page carries the right tags and parameters without a marketer having to remember them.
The editor produces two things: project data you reopen it from, and HTML plus CSS you serve. Both are yours, in formats you can read.
The editing engine. Self-hosted, BSD-3-Clause licensed, no hosted service in the path.
The JSON your pages are stored as. Readable, diffable, and yours to migrate.
What getHtml() and getCss() return — the exact markup the canvas rendered.
Auth, versioning, permissions and the publish endpoint. The part that is actually your product.
Where published pages are served from, on your own performance and caching terms.
Keep control of your project data, HTML/CSS and publishing infrastructure. Self-hosting is not a guarantee that migration is free — you still own the integration you wrote — but it does mean nothing you build sits behind someone else's export button.
The same fourteen capabilities, read twice. On the left, every row is a project. On the right, most of them are configuration — and the three that are not are labelled honestly.
| Capability | From scratch | GrapesJS |
|---|---|---|
| Canvas | Build | Included |
| Drag & drop | Build | Included |
| Components | Build | Included |
| Blocks | Build | Extensible |
| Styling | Build | Included |
| Responsive editing | Build | Included |
| Assets | Build | Extensible |
| Storage | Build | Extensible |
| Templates | Build | Extensible |
| Plugin architecture | Build | Included |
| Forms | Build | Extensible |
| SEO controls | Build | Integrate with your stack |
| A/B testing | Build | Integrate with your stack |
| Publishing | Build | Integrate with your stack |
Verified against the GrapesJS documentation and the GJS.Market catalogue on 2026-09-03. “Extensible” means there is an API and existing plugins for it; “Integrate” means the editor deliberately does not do it and your application should.
Use GrapesJS for the editor engine. Use your own application for everything specific to your business.
Real listings from the GJS.Market catalogue, grouped by the four capability areas a landing page builder is assembled from. Prices and availability are read from the catalogue at build time.
Blocks, sections and templates — what your users drag, and what they start from.
Browse categoryA starting block set to build your landing page section library on top of.
Prebuilt Tailwind sections — heroes, features, pricing — for teams already on Tailwind.
A configurable header section, the block every campaign page starts with.
A configurable footer section with navigation, legal and trust signals.
Gives your builder somewhere to save, list and reopen campaign templates.
Multi-page projects, for campaigns that need more than one URL.
Capture, findability and measurement: the parts that make a page a campaign rather than a document.
Browse categoryEditable form components users can drop into a page and point at your endpoint.
In-editor accessibility and SEO auditing, so problems surface before publish.
Wires Google Analytics into pages built in the editor.
A countdown component for time-boxed offers, launches and webinars.
Motion, typography and the components that make a page look like your brand instead of a default theme.
Browse categoryCarousels for testimonials, logo clouds and screenshot galleries.
Animated typing for hero headlines, without hand-written JavaScript in the canvas.
Easing controls in the style manager, for transitions that feel designed.
Brand typography in the editor, so what a marketer sees is what ships.
Where the project lives, how assets get in, and how the page leaves the editor.
Browse categoryProject listing and management — the layer above a single editing session.
Publishes straight to Netlify, for teams who want a hosting target on day one.
Exports the page as a downloadable archive of HTML, CSS and assets.
Backs the asset manager with Cloudinary instead of local uploads.
Three shopping lists, framed by what you are building rather than by catalogue category.
For a marketing team publishing its own pages
Drag-and-drop mechanicsFor a product team shipping page building as a feature
For an agency reusing one system across accounts
Prices are read from the catalogue at build time and may have changed — the product page is authoritative.
GrapesJS provides the visual editing foundation. GJS.Market plugins let you add specialized capabilities without building every feature yourself.
Every capability becomes a ticket, a review and a maintenance commitment.
Four steps, repeated per capability, on top of an engine you did not write.
Putting a page builder inside a product is mostly not an editor problem. The editor is one route; the rest is the multi-tenant machinery around it.
Where the editor sits
Who can open the editor, and whose pages they see when they do.
The unit a page belongs to. Usually the same unit you bill.
Edit, publish and delete are three different rights. Publishing is the one worth guarding.
Page count, seat count or publish volume — all of which the editor is happy to be unaware of.
Your chrome, your fonts, your icons. The editor UI is configuration, not a fixed interface.
Slugs, custom domains, versions and rollbacks, served from your infrastructure.
The editor is one screen in your product. Tenancy, permissions and billing are what make it a feature you can sell.
An agency's problem is not building one page builder. It is not rebuilding the same thing for the eleventh client.
Or the client's. The editor chrome, icons and typography are yours to set per tenant.
One section library, maintained once, deployed to every account.
Isolated projects under one installation, with per-client assets and templates.
Kick off a new client with the layouts that worked for the last one.
Let a client edit copy without letting them restructure the page or publish unreviewed.
Deploy to the client's domain and hosting, without the agency becoming a hosting company.
Create one editing system and reuse it across multiple client projects. The blocks and templates become the agency's asset; the client only ever sees their own brand.
GrapesJS core is published under the BSD-3-Clause licence and the official React wrapper under MIT. Both permit commercial use. The practical consequences:
The editor runs inside your application. There is no hosted editor service between you and your users.
Panels, commands and component types are all replaceable, not merely themeable.
Project data lives in your database, in a format you can read and migrate.
Build the editor interface your users need rather than the one a vendor ships.
Your CRM, your asset storage, your deploy target — wired through the plugin API.
A documented plugin architecture and an existing catalogue to draw on.
Nothing you build depends on a hosted editor continuing to exist on its current terms.
A page builder has two very different performance budgets, and confusing them is the most common mistake in this architecture.
Import it dynamically so it never lands in a shared bundle.
One editor instance, on the authoring route, mounted client-side.
The editor owns its own DOM. Re-rendering the wrapper on every keystroke fights it.
Paginate and lazy-load the asset manager rather than listing every upload.
Load a plugin when the panel that needs it opens, not on editor init.
Two entry points, two budgets. The published page should not import the editor at all.
Images, fonts and critical CSS on the output, tuned like any other production page.
// The editor is an authoring environment. Keep it out of the bundle
// that a visitor downloads to read a published landing page.
// /admin/editor — loaded only for authenticated authors.
const PageBuilder = dynamic(() => import('@/components/page-builder'), {
ssr: false, // GrapesJS measures the DOM on init
loading: () => <EditorSkeleton />,
});
// Heavy, optional plugins load when the panel that needs them opens.
async function openAssetManager(editor) {
const { default: cloudinary } = await import('grapesjs-cloudinary');
cloudinary(editor, { cloudName: process.env.NEXT_PUBLIC_CLOUDINARY });
editor.runCommand('open-assets');
}The editor is an authoring environment. Published landing pages do not necessarily need the full editor runtime — and a campaign page that ships an editor bundle to every visitor has thrown away the reason for building one.
A visual builder accepts structured input from a browser and turns it into markup you serve. Every item below follows from that one sentence.
Load, save and publish endpoints are privileged. Treat them that way.
Being allowed to edit a page is not the same as being allowed to publish it.
Check type, size and content server-side, and serve user uploads from an origin you are willing to isolate.
Where page authors are not fully trusted, sanitize on the way out as well as in.
Project JSON arriving at your API is untrusted input. Parse it, do not simply store it.
Publishing changes what the public sees. Rate-limit it, log it, and make it revertible.
Anything the editor sends can be forged. Re-derive ownership and limits on the server.
This is a starting checklist, not a guarantee. Threat-model your own deployment — especially if the people editing pages are not the people who own the domain.
Three stages, each shippable. The mistake is trying to reach the third one before anybody has published a page from the first.
One person can build and publish a page.
A team can rely on it without you in the room.
The builder becomes something you can sell.
Every stage after the first is a product decision, not an editor decision — which is exactly why the editor should not be the thing you are still building.
Start with the GrapesJS visual editing engine, customize it for your product and add the capabilities you need with GJS.Market plugins.
Install the core, register your first section library, and have a page rendering from project data.
Get StartedAdd blocks, templates, forms, SEO auditing and publishing targets without writing them yourself.
Browse PluginsTell us what the builder has to do and we will scope the editor, the plugin stack and the integration.
Build Your Landing Page BuilderBuild the editor your product needs — instead of rebuilding the editor engine from scratch.