What the Storage Manager does
The GrapesJS Storage Manager persists the editor’s project data — the full,
reloadable representation of the page. The simplest production setup is
remote storage pointed at your backend; for full control you can
register a custom adapter.
1. Remote storage
const editor = grapesjs.init({
container: '#gjs',
storageManager: {
type: 'remote',
autosave: true,
stepsBeforeSave: 3, // save after 3 changes
options: {
remote: {
urlStore: '/api/page', // POST project JSON here
urlLoad: '/api/page', // GET project JSON here
headers: { 'Authorization': 'Bearer ' + token },
},
},
},
});
GrapesJS POSTs the project to urlStore and loads it from
urlLoad. Add framework CSRF headers here when needed.
2. Save manually instead of autosaving
// storageManager: { type: 'remote', autosave: false, ... }
document.getElementById('save').onclick = () => editor.store();
3. A fully custom storage adapter
editor.Storage.add('my-store', {
async load() {
const res = await fetch('/api/page');
return res.ok ? res.json() : {};
},
async store(data) {
await fetch('/api/page', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
});
},
});
// then:
grapesjs.init({
container: '#gjs',
storageManager: { type: 'my-store' },
});
Production tips
Tune saving for real use. stepsBeforeSave controls how chatty autosave is — too low and you hammer your API on every keystroke-like change; a value of 3–5 is usually right, or switch to a manual save button. Send your auth (Bearer token or CSRF) in the headers option so the store route stays protected. If multiple people can edit the same page, add a version or updated_at check on the server and reject stale writes rather than silently overwriting. And handle the failure path: surface a visible error (and keep the in-memory project) when a save request fails, so work isn't lost.
Prerequisites
You need a running GrapesJS editor and a backend endpoint (any language). The Storage Manager persists the editor's project data — the full, reloadable representation of the page — so you can re-open exactly what was saved.
Handle multiple pages
Most apps store more than one page. Pass an id to your endpoints and key the record by it, either via the URL or a custom header:
storageManager: {
type: 'remote',
options: { remote: {
urlStore: `/api/pages/${pageId}`,
urlLoad: `/api/pages/${pageId}`,
headers: { Authorization: `Bearer ${token}` },
}},
}
Custom adapter with error handling
editor.Storage.add('api', {
async load() {
const res = await fetch(`/api/pages/${pageId}`);
if (!res.ok) throw new Error('load failed');
return res.json();
},
async store(data) {
const res = await fetch(`/api/pages/${pageId}`, {
method: 'POST', headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
});
if (!res.ok) throw new Error('save failed');
},
});
Tune autosave and surface failures
stepsBeforeSave controls how chatty autosave is — 3–5 is usually right, or
use a manual save button (autosave: false + editor.store()).
Listen for storage errors and show the user a visible message while keeping the
in-memory project, so a failed request never loses work.
Concurrency and best practices
If multiple people can edit the same page, add a version or updated_at
check on the server and reject stale writes instead of silently overwriting. Always
authenticate the store route. Store the full project plus rendered html/css so pages
serve fast and still re-open in the editor.
Next steps
See backend-specific wiring in the Laravel, Django, and Express/Node guides, browse storage adapter plugins, or start from the GJS.Market home page.
FAQ
How does the Storage Manager save to a backend?
Set type: 'remote' with options.remote.urlStore and
urlLoad; GrapesJS POSTs/GETs the project JSON, optionally with auth
headers.
How do I control autosave?
Use autosave: true with stepsBeforeSave, or set
autosave: false and call editor.store() yourself.
Can I write a custom adapter?
Yes — editor.Storage.add('name', { load, store }) and set
storageManager.type to that name.