Skip to main content
Do not use LocalObjectStoragePipeline in production.
Use a custom API-backed pipeline and register it through a CKEditor plugin.

Step 1: create a custom storage pipeline

import type { GruntObjectStoragePipeline } from "@grunt/ckeditor5-grunt/grunt";

export class CustomStoragePipeline implements GruntObjectStoragePipeline {
  async saveCommit(key: string, commit: string): Promise<void> {
    await fetch(`/api/grunt/${key}/commit`, {
      method: "PUT",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ commit })
    });
  }

  async saveSvg(key: string, svg: string): Promise<void> {
    await fetch(`/api/grunt/${key}/svg`, {
      method: "PUT",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ svg })
    });
  }

  async getCommit(key: string): Promise<string> {
    const response = await fetch(`/api/grunt/${key}/commit`);
    if (!response.ok) throw new Error("Failed to load commit");
    const data = await response.json();
    return data.commit;
  }

  async getSvg(key: string): Promise<string | null> {
    const response = await fetch(`/api/grunt/${key}/svg`);
    if (response.status === 404) return null;
    if (!response.ok) throw new Error("Failed to load svg");
    const data = await response.json();
    return data.svg ?? null;
  }
}
You can add:
  • Authentication headers
  • JWT handling
  • Versioning
  • Multi-tenant routing
  • Signed URLs
  • Retry logic

Step 2: register the pipeline in a plugin

import { Plugin } from "ckeditor5";
import Grunt from "@grunt/ckeditor5-grunt/grunt";
import { CustomStoragePipeline } from "./CustomStoragePipeline";

class CustomGruntPipelinePlugin extends Plugin {
  static get pluginName() {
    return "CustomGruntPipelinePlugin";
  }

  static get requires() {
    return [Grunt];
  }

  init() {
    const editor = this.editor;

    editor.plugins.get("Grunt").setPipeline(new CustomStoragePipeline(editor));
  }
}

Step 3: include the plugin in your editor config

ClassicEditor.create(document.querySelector("#editor"), {
  plugins: [
    Essentials,
    Paragraph,
    Bold,
    Italic,
    Grunt,
    CustomGruntPipelinePlugin
  ]
});
Grunt now uses your API for object persistence.

Next: runtime assets

Serve grunt.js and _framework/* correctly.