Next.js Page Builder — GrapesJS Integration Guide

Add visual editing to your Next.js application

22k+
GitHub Stars
100+
Plugins
MIT
License
10yrs+
In Production

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.

Browse Next.js-compatible plugins →

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

1

Install GrapesJS

Run npm install grapesjs in your Next.js project. TypeScript types are included.

2

Create a client-only editor component

Create GrapesEditorClient.tsx that initialises GrapesJS in a useEffect hook with a DOM ref.

3

Wrap with next/dynamic (ssr: false)

Use dynamic(() => import("./GrapesEditorClient"), { ssr: false }) to prevent server-side execution.

4

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$19

Storage REST API

Connect GrapesJS to Next.js API routes for persistence

Blocks$29

Blocks Library Pro

Production-ready block library for Next.js page builders

EmailFree

GrapesJS MJML Preset

Build responsive email templates in your Next.js app

ExportFree

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 });
}

Ship your Next.js page builder faster

Browse all Next.js-compatible plugins and starters on GJS.Market.

Browse Next.js-compatible plugins →