GrapesJS is the most powerful open-source drag-and-drop editor framework for JavaScript developers. Adding it to a Next.js application requires one important consideration — GrapesJS needs the browser DOM to initialise, so it cannot run server-side. With the correct next/dynamic setup, you get a fully functional visual editor in your Next.js app in under an hour. GJS.Market plugins handle storage, blocks, and UI — so you can focus on your product.
Important: GrapesJS is browser-only
GrapesJS calls browser APIs (document, window, requestAnimationFrame) on initialisation. You must use dynamic() with { ssr: false } in the Pages Router, or the 'use client' directive with an isMounted guard in the App Router. Skipping this will cause a build-time or runtime error.
Client-Side Only Pattern — Dynamic Import with ssr: false
The recommended approach for both Pages Router and App Router is to split the editor into two files: a thin wrapper that uses dynamic to disable SSR, and a client-only component that initialises GrapesJS:
// components/GrapesJSEditor.tsx
import dynamic from 'next/dynamic';
const GrapesEditor = dynamic(
() => import('./GrapesEditorClient'),
{ ssr: false, loading: () => <div>Loading editor...</div> }
);
export default function GrapesJSEditor() {
return <GrapesEditor />;
}
// components/GrapesEditorClient.tsx
import { useEffect, useRef } from 'react';
import grapesjs from 'grapesjs';
export default function GrapesEditorClient() {
const ref = useRef<HTMLDivElement>(null);
useEffect(() => {
const editor = grapesjs.init({
container: ref.current!,
storageManager: false,
});
return () => editor.destroy();
}, []);
return <div ref={ref} style={{ height: '100vh' }} />;
}App Router vs Pages Router
Pages Router (Next.js 12/13)
Use dynamic() from next/dynamic with { ssr: false }. All components are React Client Components by default, so the only requirement is disabling SSR for GrapesJS.
App Router (Next.js 13+)
Add 'use client' at the top of your editor component. You can still use dynamic() for the loading state, or add an isMounted state guard to prevent rendering until the component has mounted client-side.
Step-by-Step Integration Guide
Install GrapesJS
Run npm install grapesjs in your Next.js project. TypeScript types are included.
Create a client-only editor component
Create GrapesEditorClient.tsx that initialises GrapesJS in a useEffect hook with a DOM ref.
Wrap with next/dynamic (ssr: false)
Use dynamic(() => import("./GrapesEditorClient"), { ssr: false }) to prevent server-side execution.
Connect storage to an API route
Use editor.getHTML() and editor.getCSS() to post content to a Next.js API route, Vercel KV, or Supabase.
Recommended Plugins for Next.js Projects
Storage REST API
Connect GrapesJS to Next.js API routes for persistence
Blocks Library Pro
Production-ready block library for Next.js page builders
GrapesJS MJML Preset
Build responsive email templates in your Next.js app
Export Plugin
Export clean HTML/CSS from within Next.js API routes
Storage Integration Examples
Vercel KV
// Save to Vercel KV
import { kv } from '@vercel/kv';
export async function POST(req: Request) {
const { id, html, css } = await req.json();
await kv.set(`page:${id}`, { html, css });
return Response.json({ ok: true });
}Supabase
// Save to Supabase
import { createClient } from '@supabase/supabase-js';
const supabase = createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.SUPABASE_SERVICE_KEY!
);
export async function savePage(id: string, html: string, css: string) {
await supabase.from('pages').upsert({ id, html, css });
}Related Guides
Ship your Next.js page builder faster
Browse all Next.js-compatible plugins and starters on GJS.Market.
Browse Next.js-compatible plugins →