Drag and drop
Users move blocks and components onto a canvas instead of editing a template file. You decide which blocks exist, where they may be dropped and what they may contain.
PageKit — the self-hosted GrapesJS site builder, sold as source. Get early access
Visually build HTML pages with drag-and-drop components, edit styles without writing CSS by hand, and keep control of the HTML and CSS your application produces.
26k+
GitHub stars
1.4M+
Monthly npm downloads
100+
Plugins on GJS.Market
BSD-3-Clause
Core license
This is the whole argument of the page in one component. The Visual tab is what your user drags and edits. The other two are what the editor hands back to your code — nothing hidden, nothing proprietary, just markup and stylesheet text you can store, transform and publish however you like.
<div id="editor">What the user sees: blocks on the left, the page in the middle, style controls on the right.
Blocks
Build faster
Launch your next project.
Get startedStyles
<body>
<section class="hero">
<h1 class="hero__title">Build faster</h1>
<p class="hero__text">Launch your next project.</p>
<a href="/start" class="hero__cta">Get started</a>
</section>
</body>* { box-sizing: border-box; } body {margin: 0;}
.hero{
padding-top:96px;
padding-right:24px;
padding-bottom:96px;
padding-left:24px;
text-align:center;
background-color:rgb(15, 23, 42);
}
.hero__title{
font-size:48px;
color:rgb(255, 255, 255);
}
.hero__cta{
display:inline-block;
padding-top:14px;
padding-right:28px;
padding-bottom:14px;
padding-left:28px;
border-top-left-radius:8px;
border-top-right-radius:8px;
border-bottom-right-radius:8px;
border-bottom-left-radius:8px;
background-color:rgb(99, 102, 241);
color:rgb(255, 255, 255);
}
@media (max-width: 480px){
.hero__title{ font-size:32px; }
}Both code panes are the real return values of GrapesJS 0.23.6, re-indented for reading — each call actually returns a single line. Two details worth knowing before you build on them: getHtml() serializes the wrapper, so the result is enclosed in <body>…</body>, and getCss() writes shorthand declarations out as longhands and normalizes colors to rgb(). The stylesheet also starts with a small reset GrapesJS injects; getCss({ avoidProtected: true }) leaves it out.
An HTML drag-and-drop builder lets users assemble page structures visually while your application remains in control of the underlying content, styling and publishing workflow.
Users move blocks and components onto a canvas instead of editing a template file. You decide which blocks exist, where they may be dropped and what they may contain.
Content, layout and styles are edited through panels rather than by hand-writing every CSS rule. The editor turns those choices into ordinary stylesheet rules.
Retrieve the resulting HTML and CSS as two plain strings and take them into your own application, your own storage and your own publishing workflow.
It is an editor that lets a person assemble a web page by dragging pieces onto a canvas, adjusting them through controls rather than code, and getting real HTML and CSS out the other side. To do that convincingly, it has to combine all of the following into one coherent piece of software:
The difference between a visual HTML editor and a simple drag-and-drop library is that the editor manages the entire editing state — selection, components, styles, commands, storage and output — as one system. A library moves a box across the screen; an editor knows what that box is, what it may contain, which CSS rule belongs to it, and how to serialize the result.
Three public GrapesJS builds. Nothing loads until you click — the frames stay unrequested so this section costs you nothing on arrival.
The stock GrapesJS demo. Drag a block in, select it, change a style — then open the code view in its toolbar and watch the HTML and CSS change with you.
Loads a third-party demo in an iframe. Nothing is requested until you click.
Choose a block
The user picks from the palette you defined. A block is a named, reusable starting point — a hero, a pricing table, a form — not an arbitrary empty div.
Drag it into the canvas
The editor resolves where the block may legally land, inserts the component and selects it. Drop rules are yours to set per component type.
Edit content and styles
Text is edited in place; layout, spacing, typography and color come from the Style Manager. Every change becomes a CSS rule the editor tracks.
Save the project
The Storage Manager sends the editor state to whatever endpoint you point it at. This is the state needed to keep editing, not the finished page.
Export or publish HTML + CSS
Call getHtml() and getCss(), then do whatever your product requires: sanitize, wrap in your layout, version, cache, deploy.
Exactly as much as you allow. Every one of these is something the editor can expose — and something you can lock down per component type so a marketer cannot break a layout an engineer designed.
Text, headings, links and any content you mark editable. Components you would rather keep intact can simply be left non-editable.
Sections, columns, containers and the spacing between them, through the Dimension and Flex controls in the Style Manager.
Font family, size, weight, line height and alignment, written out as ordinary CSS declarations.
Text, backgrounds, borders and other visual properties. Values are serialized as rgb() in the emitted stylesheet.
Handled through the Asset Manager, which you can point at your own upload endpoint or a storage provider.
Rules scoped to a device. Styling while a breakpoint is active emits a matching media query rather than overwriting the base rule.
Structured, reusable elements defined by you — the unit a block turns into once dropped, and the thing your app can recognize later.
Traits become the fields in the settings panel, so a custom component can expose a headline and a link target and nothing else.
Some of these are configuration rather than defaults. GrapesJS ships the Style Manager with General, Flex, Dimension, Typography, Decorations and Extra sectors out of the box; asset uploads, rich-text behaviour and per-component permissions are things you wire up — or install a plugin for.
A visual editor is only useful if the result can fit into your application. GrapesJS keeps two things separate, and the distinction matters more than any feature list: the state it needs in order to keep editing, and the markup your users actually receive.
From canvas to live page
// What the editor needs to keep editing — components, styles,
// pages, assets and symbols. Store this to resume a session.
const projectState = editor.getProjectData();
// What your users actually receive. Two plain strings.
const html = editor.getHtml(); // <body>…</body>
const css = editor.getCss(); // one line of CSS
// From here it is your application's decision: sanitize it, wrap it
// in your own layout, version it, cache it, put it behind your CDN.
await fetch(`/api/pages/${pageId}/publish`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ html, css }),
});A note on expectations: the emitted stylesheet is faithful, not hand-tuned. Shorthands come out as longhands, colors as rgb(), and a small reset is prepended unless you disable it. Treat the output as a reliable source you post-process, the same way you would treat the output of any build step.
Every visual HTML editor needs the same set of subsystems before it can do anything interesting. The question is not whether you will need them — it is whether writing them is the part of your product worth your team's next six months.
What an HTML editor needs before it edits anything
And you own them permanently: the canvas that has to stay editable while rendering like a browser, the drop-target logic, the undo stack that survives everything else, the serializer that turns your in-memory tree back into markup — plus every bug report against them for as long as the product lives.
The canvas, drag and drop, component model, Block Manager, Style Manager, Device Manager, Asset Manager, undo history, Storage Manager and the HTML/CSS output APIs come with the core. What you write is the part that is specific to your product.
This is not a claim that GrapesJS is your whole product. Storage, authentication, permissions, publishing, sanitization and every product-specific workflow remain yours — see the architecture diagram below for exactly where the line falls.
The right column is not a promise that a subsystem is finished for your use case — it names the part of GrapesJS you extend instead of the blank file you would otherwise start from.
| Editor subsystem | Build yourself | Start with GrapesJS |
|---|---|---|
| Canvas | Build | Editor foundation |
| Drag and drop | Build | Editor foundation |
| Component model | Build | DomComponents |
| Blocks | Build | Block Manager |
| Style controls | Build | Style Manager |
| Responsive editing | Build | Device Manager |
| Assets | Build | Asset Manager |
| Undo / redo | Build | Commands and history |
| Storage | Build | Storage Manager |
| HTML output | Build | editor.getHtml() |
| CSS output | Build | editor.getCss() |
| Custom components | Build | DomComponents.addType |
| Plugin architecture | Build | Existing plugin API |
| Auth and permissions | Build | Your application |
| Sanitization | Build | Your application |
| Publishing | Build | Your application |
The last three rows are the honest ones. Nothing about embedding an editor removes your responsibility for who may edit what, what gets stored, and what reaches the public.
The editor is one layer of your product — not the product itself. Your application keeps the users, the permissions, the data and the decision about what goes live.
Your application
You own the surface
Persistence
Your API
Data
Your database
Delivery
Your publishing
Five managers do most of the work. You will meet them within an hour of starting, so it is worth knowing what each is responsible for.
HTML elements represented as editable components, with their own rules about what they contain, whether they can be dragged and which properties they expose.
The palette. Blocks are what users drag; each one names the component it becomes. The core ships an empty palette, so what appears there is entirely your decision.
Visual CSS property controls, grouped into sectors — General, Flex, Dimension, Typography, Decorations and Extra by default. Sectors and properties are configurable.
Persists project state to local storage, a remote endpoint, or a storage adapter you write yourself. Autosave is a config flag.
Images and media: the picker users see and the upload path behind it, which you point at your own endpoint or a provider plugin.
This is where an HTML builder stops being generic. Define a component type, decide what it renders, and expose only the properties users should control — the rest of the markup stays exactly as your engineers wrote it.
// A block is what the user drags out of the panel.
editor.BlockManager.add('hero', {
label: 'Hero',
category: 'Sections',
content: { type: 'hero' },
});
// A component type is what that block becomes on the canvas — and the
// only place you decide what the user may change about it.
editor.DomComponents.addType('hero', {
model: {
defaults: {
tagName: 'section',
attributes: { class: 'hero' },
components: '<h1 class="hero__title">Build faster</h1>',
// Traits become the fields in the settings panel.
traits: [{ type: 'text', name: 'headline', label: 'Headline' }],
droppable: false,
},
},
});Four things share a name in most tutorials and are genuinely different. Keeping them apart is the difference between a builder you can maintain and a pile of HTML strings.
Block
A palette entry: a label, a category and the content it inserts. Purely a starting point — it has no life after the drop.
Component
The type registered with DomComponents. It decides the tag, the default children, whether the thing is droppable, and how the editor treats it from then on.
Traits
The fields in the settings panel. A trait is a deliberate hole in an otherwise sealed component — a headline, a link target, a plan id.
Markup
The component serialized back to HTML, with the classes and attributes you defined, ready for whatever your backend does next.
Design this chain deliberately and non-technical users get real freedom inside boundaries you chose. Skip it and you have shipped a text editor that emits divs.
These are the section types nearly every builder needs on day one, described by the markup they emit rather than by how they look. Build them against the Block Manager API, or install a block plugin and start from a full palette.
A section with a heading, a supporting paragraph and a link styled as a button.
A header with a logo element and a nav list that collapses on narrow widths.
A container of repeated cards, each with an icon slot, a heading and body copy.
Plan cards with a price element, a feature list and a call-to-action link.
Blockquote elements with a citation and an optional avatar image.
An image grid whose sources are filled from the Asset Manager.
A single centred section with one heading and one primary link.
A form element with labelled inputs and an action you point wherever you like.
Link columns, legal text and secondary navigation in a footer element.
Worth being precise about: these are examples of block types, not a list of things built into GrapesJS. The core starts with an empty Block Manager on purpose — which palette your users get is a decision you make, either by writing the blocks or by installing one of the block plugins further down this page.
The same editing core, pointed at six different problems. Each of these needs HTML-level control for a different reason.
Give users a visual way to edit pages that already exist as HTML. Because the editor round-trips markup, you are extending your current pages rather than migrating them into someone's proprietary format.
WYSIWYG page builderLet marketers assemble campaign pages from blocks your team designed, with the classes and structure your design system expects still intact in the output.
Landing page builderEmbed visual editing as a feature of your product. Customers build pages inside your app, on your domain, stored in your database.
SaaS page builderAdd a visual layer to a content platform without replacing it. The editor produces HTML the CMS already knows how to store and render.
Headless CMS editorEmail HTML is its own discipline — tables, inline styles, client quirks. GrapesJS handles it through the MJML and newsletter presets rather than the web-page path.
React email builderShip the editor inside an existing dashboard, behind your own authentication and navigation, with no redirect to a third-party site.
Embeddable page builderDifferent teams arrive here for different reasons, but the requirement is the same: visual editing that does not cost them control of the markup.
Add visual editing to an application that already exists, without adopting a platform or rewriting the front end around someone else's runtime.
Turn page creation into a feature customers pay for, with the editor living on your domain and the data staying in your database.
Give content teams visual editing without replacing the CMS underneath, because what comes out is HTML the system already handles.
Build one page-editing system with your own blocks and components, then reuse it across client projects instead of rebuilding per engagement.
Let users compose landing pages visually while your platform keeps the templates, the tracking and the publishing pipeline consistent.
You do not have to send users to another website to edit their pages. Mount the editor inside your own application and keep your existing authentication, navigation and backend.
Where the editor sits
In practice this is a route in your app that renders a container and initializes the editor against it. The user never leaves your domain, the session is the one they already have, and the project loads and saves through endpoints you wrote.
Point the Storage Manager at your API and every page becomes a row in your database, behind your own authentication. GrapesJS does not host anything, and neither does GJS.Market — no user content ever reaches us.
Two things to store, not one
const editor = grapesjs.init({
container: '#editor',
// Editor state goes to your API, behind your own authentication.
storageManager: {
type: 'remote',
autosave: true,
options: {
remote: {
urlStore: `/api/pages/${pageId}`,
urlLoad: `/api/pages/${pageId}`,
fetchOptions: { credentials: 'include' },
},
},
},
});Keep the distinction sharp. Editor state is what the editor needs to reopen a page for further editing. Published output is the HTML and CSS a visitor receives. They have different lifecycles, different access rules and usually different tables — and a plugin can help with the wiring, but the architecture is yours.
Edit
The user works on the canvas. Nothing is public yet; the editor is only touching project state.
Save a draft
Autosave or an explicit save writes the state through your storage endpoint, under your permission rules.
Preview
Render the current HTML and CSS on a preview route so the author sees the real page, not the canvas.
Approve
If your product needs review, this is your workflow — the editor has no opinion about who may sign off.
Publish
Take getHtml() and getCss(), sanitize, wrap in your layout, version the result and write it wherever you serve pages from.
Live HTML
The published page is plain HTML and CSS on your infrastructure. No editor runtime is required for a visitor to view it.
This is the clearest difference from a hosted page builder. There is no vendor deciding when your page goes live, on which domain it is served, or what happens to it if you stop paying.
The Device Manager switches the canvas between widths, and styles set while a device is active are written as a media query scoped to that width instead of overwriting the base rule. GrapesJS ships with Desktop, Tablet, Mobile landscape and Mobile portrait; the list and its widths are configuration, not a fixed set.
What users can change per breakpoint
The widths shown are the GrapesJS defaults. Worth stating plainly: the editor defines the CSS rules — how a published page finally renders still depends on the markup and stylesheet you ship and the browser reading them.
Everything below is a real listing on GJS.Market, with the name, price and thumbnail rendered live from the catalogue. Grouped the way this page thinks about the problem: code and output first, then the panels around them.
Let users — or your own developers — read and edit the markup and styles behind a component without leaving the editor.
Browse this categoryOpens the HTML and CSS of the selected component for direct editing — the fastest way to make the visual/code relationship obvious to a sceptical developer.
A code editing surface inside the builder for users who want to drop to markup for one section and stay visual everywhere else.
Get the HTML and CSS out: as a downloadable bundle, converted server-side, or pushed straight to a host.
Browse this categoryPackages the project's HTML, CSS and assets into a downloadable archive, for handing off a static site.
Converts stored project JSON into HTML and CSS on the server, so publishing does not require a browser session.
Pushes the generated output to a host directly from the editor, for products that want one-click publishing.
Ready-made palettes, so the empty Block Manager is filled with sections rather than a blank panel.
Browse this categoryA starting palette of common structural blocks, so day one is not spent writing Block Manager calls.
Blocks that emit Tailwind utility classes, for teams whose output has to match an existing Tailwind design system.
Better CSS editing: class suggestions while typing, richer color and gradient pickers.
Browse this categorySuggests existing CSS classes while typing in the Selector Manager, which keeps generated markup consistent rather than sprawling.
A gradient picker for the Style Manager, covering a case the default color control does not.
Reusable, synchronized elements and tooling for turning your own markup into an editor component.
Browse this categoryExtend the inline text editor so content editing feels like a document rather than a code field.
Browse this categoryConnect the Asset Manager to real storage so images have somewhere to live.
Browse this categoryPersistence adapters for teams who would rather configure a backend than write one.
Browse this categoryPreconfigured setups and template management, for starting from a working editor instead of an empty one.
Browse this categoryEmail HTML has its own rules. The MJML path exists for exactly that reason.
Browse this categoryThree starting configurations, assembled only from listings that actually exist. Treat them as a shape to adapt, not a bundle to buy.
For developers who need a simple visual editor and nothing more
See the WYSIWYG guideFor multi-page sites with a real design system behind them
See the landing page guideFor customer-facing editing inside your own product
Prices render live from the catalogue.
These are not four products competing for the same job — they are four different trades. The point of the table is to make the trade explicit, not to declare a winner.
| Consideration | GrapesJS | Drag-and-drop library | Hosted page builder | Build from scratch |
|---|---|---|---|---|
| Visual editor included | Yes | Partial — the gesture only | Yes | Whatever you build |
| Control over HTML and CSS | High — output is yours to post-process | High — you write the markup | Varies by vendor | Total |
| Self-hosted | Yes | Yes | Usually not | Yes |
| Embeddable in your app | Yes | Yes | Depends on the vendor | Yes |
| Component model and traits | Built in | You write it | Vendor's model | You write it |
| Style controls and breakpoints | Built in | You write them | Built in | You write them |
| Time to a working editor | Days | Months | Minutes | Months to years |
| Product customization | High | High | Bounded by the vendor | Unbounded |
| Ongoing maintenance | Your integration, plus an upstream core | All of the editor | Vendor's | All of it, forever |
| License and cost | BSD-3-Clause core, $0 to use | Usually permissive | Subscription | Engineering time |
Verified against the GrapesJS core (BSD-3-Clause, v0.23.6) on 2026-09-03. Hosted-builder rows are deliberately vague because their capabilities differ per vendor and change often — check the specific product you are considering rather than trusting a category-level claim.
This is the single most expensive misjudgement in this space. A team picks a sortable-list library, ships a demo in a week, and then spends a year discovering what was missing.
Everything needed to move a box from one place to another, and nothing beyond it. Genuinely useful — for reordering a list.
State that persists across every one of those gestures, and a serializer that can turn all of it back into markup a browser will render.
Drag and drop is one interaction. A page builder is an entire editing system.
One package, one container, one init call. Everything on this page grows from these few lines.
npm install grapesjsimport grapesjs from 'grapesjs';
import 'grapesjs/dist/css/grapes.min.css';
// Mount the editor on any container in your own application.
const editor = grapesjs.init({
container: '#editor',
height: '100vh',
// Core ships no blocks: you decide what users are allowed to drag.
blockManager: { blocks: [] },
});The editor now runs but has an empty palette — the core deliberately ships no blocks. Register the first one, and the component type it becomes:
// A block is what the user drags out of the panel.
editor.BlockManager.add('hero', {
label: 'Hero',
category: 'Sections',
content: { type: 'hero' },
});
// A component type is what that block becomes on the canvas — and the
// only place you decide what the user may change about it.
editor.DomComponents.addType('hero', {
model: {
defaults: {
tagName: 'section',
attributes: { class: 'hero' },
components: '<h1 class="hero__title">Build faster</h1>',
// Traits become the fields in the settings panel.
traits: [{ type: 'text', name: 'headline', label: 'Headline' }],
droppable: false,
},
},
});From here the work is product work: which blocks your users get, what they are allowed to change, and what your backend does with the markup that comes out.
This page covers HTML-level editing and output. The neighbouring guides take the same core from other angles.
The broader guide to building a drag-and-drop page builder: the gesture, the blocks, the templates and the product decisions around them.
Read the guideHow to evaluate an open-source page builder — licensing, governance, extensibility and what self-hosting actually commits you to.
Compare the optionsBuilding a page builder inside a React application, and where a React-component model and an HTML-component model diverge.
Read the guideThe official React wrapper: mounting the editor as a component, managing its lifecycle and keeping it out of React's render loop.
See the integrationStart with GrapesJS, give users a visual HTML editing experience and keep control of your application, data and publishing workflow.
One npm package and an init call. You will have a working canvas before you finish your coffee.
Try GrapesJSBlock palettes, code views, style controls, storage adapters and export tooling — real listings, real prices.
Browse GJS.Market pluginsComponents, blocks, styles, storage and the plugin API, straight from the upstream project.
Read the builder guideDrag. Drop. Edit HTML. Own the output.