How to integrate GrapesJS into a Ruby on Rails app (complete guide 2026)

Embed GrapesJS in a Rails app: render it in an ERB view, persist content to a controller with the Rails CSRF token, and export clean HTML/CSS.

DevFuture Development
DevFuture Development
May 25, 2026a month ago
7 min read4 views

Why GrapesJS fits Rails

GrapesJS is a browser-only, MIT-licensed editor, so Rails serves the editor page and exposes load/store endpoints while GrapesJS handles the UI. This guide builds a working setup: an ERB view that hosts the editor, a controller that persists content with CSRF protection, and HTML/CSS export.

1. Render the editor in a view

Your Rails layout already includes csrf_meta_tags. Create app/views/editor/show.html.erb:

<div id="gjs"><h1>Edit me in GrapesJS</h1></div>
<link href="https://unpkg.com/grapesjs/dist/css/grapes.min.css" rel="stylesheet">
<script src="https://unpkg.com/grapesjs"></script>
<%= javascript_include_tag "editor" %>

2. Initialise GrapesJS with remote storage

// app/javascript/editor.js
const csrf = document
  .querySelector('meta[name="csrf-token"]')
  .getAttribute('content');

const editor = grapesjs.init({
  container: '#gjs',
  height: '100vh',
  fromElement: true,
  storageManager: {
    type: 'remote',
    stepsBeforeSave: 3,
    options: {
      remote: {
        urlStore: '/editor/save',
        urlLoad: '/editor/load',
        headers: { 'X-CSRF-Token': csrf },
      },
    },
  },
});

3. Routes and controller

# config/routes.rb
get  "editor",      to: "editor#show"
get  "editor/load", to: "editor#load"
post "editor/save", to: "editor#save"
# app/controllers/editor_controller.rb
class EditorController < ApplicationController
  def show; end

  def load
    page = Page.find_or_create_by(key: "home")
    render json: (page.project || {})
  end

  def save
    page = Page.find_or_create_by(key: "home")
    page.update!(
      project: params.permit!.to_h,
      html: params["gjs-html"],
      css: params["gjs-css"]
    )
    render json: { status: "ok" }
  end
end

The X-CSRF-Token header satisfies Rails' default forgery protection, so you keep CSRF on. Store project as a jsonb column.

4. Render the exported page

<style><%= raw @page.css %></style>
<%= raw @page.html %>

Only use raw for trusted editors; sanitise otherwise.

Production tips

Laptop with a code editor open on a desk
Store the project as jsonb and serve cached HTML.

For a solid Rails setup, store the GrapesJS project in a jsonb column so queries and partial updates stay cheap, and keep the rendered html/css in their own columns for fast serving. If you load GrapesJS through importmap-rails rather than a CDN, pin it with bin/importmap pin grapesjs and confirm it resolves in production where the asset host differs. Keep Rails' forgery protection on — the X-CSRF-Token header from csrf_meta_tags satisfies it. And cache the published page (fragment cache or a CDN), invalidating on save, so the editor's render cost never reaches end users.

Prerequisites

You'll need Ruby 3.x and a Rails 7 application. GrapesJS can be loaded from a CDN in an ERB view or pinned with importmap-rails — no Rails-specific gem is needed. The editor runs in the browser and posts to your controllers, so familiarity with views, routes, controllers, and Rails' forgery protection is enough.

Add custom blocks to the editor

Register draggable blocks with the Block Manager after grapesjs.init:

editor.BlockManager.add('hero', {
  label: 'Hero section',
  category: 'Sections',
  content: '<section class="hero"><h1>Headline</h1><p>Copy</p></section>',
});

Pull ready-made block libraries and presets from GJS.Market for a richer set.

Storage deep-dive: a custom adapter

A custom storage adapter reads the CSRF token from the meta tag and posts to your routes:

const csrf = document.querySelector('meta[name="csrf-token"]').content;
editor.Storage.add('rails-store', {
  async load() {
    const res = await fetch('/editor/load');
    return res.ok ? res.json() : {};
  },
  async store(data) {
    await fetch('/editor/save', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': csrf },
      body: JSON.stringify(data),
    });
  },
});
// then: storageManager: { type: 'rails-store' }

Store the project in a jsonb column so it re-loads into the editor exactly.

Performance tips

Load GrapesJS only on the editor screen and defer the script. Cache the published page with Rails fragment caching or a CDN and invalidate on save. If you load GrapesJS through importmap-rails, confirm the pin resolves in production where the asset host differs.

Security considerations

Keep forgery protection on — the X-CSRF-Token header from csrf_meta_tags satisfies it. Add a before_action that authorises the current user before saving. Only use raw/html_safe on stored markup when editors are trusted; otherwise sanitise on output. Validate the payload size.

Troubleshooting common errors

422 Unprocessable Entity usually means the CSRF token is missing or stale. An unstyled canvas means the stylesheet didn't load. A blank editor means the container selector matched nothing. 413 means the project payload exceeds the body limit.

When to use GrapesJS with Rails

GrapesJS fits when your Rails app needs an embedded visual editor your users control — landing pages, emails, a white-label builder — backed by your own database. For inline rich text, a lighter WYSIWYG is enough; for full-page composition with layout, styling, and HTML/CSS export, GrapesJS is the stronger, MIT-licensed, self-hosted choice.

Next steps

See the related GrapesJS + Laravel and GrapesJS + Django guides, browse the GrapesJS marketplace, or start from the GJS.Market home page.

FAQ

How do I save GrapesJS content to a Rails backend?

Configure the Storage Manager with type: 'remote' and point it at Rails routes. Send the csrf-token meta value as the X-CSRF-Token header, then store the project JSON on a model.

How do I load GrapesJS without a heavy build step?

Include it from a CDN in an ERB view, or pin it with importmap-rails — both work because GrapesJS is plain browser JavaScript.

Where should I store the project?

Use a jsonb column for the full project, plus optional text columns for the rendered html and css.

More tags:
Published May 25, 2026
Updated Jun 27, 2026
🤖 AI

Adding AI to your GrapesJS editor?

Find AI-powered plugins for content generation, smart layouts, and image suggestions.

Share this postTwitterFacebookLinkedIn
Published via
DevFuture Development
DevFuture Development
Visit shop →

More from DevFuture Development

Discover other insightful posts and stay updated with the latest content.

View all posts

Premium plugins from DevFuture Development

Hand-picked paid additions crafted by this creator.

Visit shop →