ReadyEditor ReadyEditor
docs/embedding-guide.md

Embedding Guide

The loader is a single script tag that handles everything: it calls the init endpoint, downloads the correct assets for your plan, and initializes the editor.

<script
  src="https://cdn.readyeditor.io/releases/<version>/js/readyeditor.loader.min.js"
  data-api-key="rk_..."
  data-selector=".my-editor"
  data-load-css="1"
  data-plugins="auto"
  data-require-sri="1"
  data-auto-init="1"
></script>

Key points:

  • data-init-endpoint defaults to /api/v1/editor/init resolved against the loader script origin. This enables cross-origin embedding: your host page can be on a different domain than the ReadyEditor CDN.
  • When data-require-sri="1", the loader enforces SRI integrity on every loaded asset using hashes from the release manifest.
  • Set data-require-sri="0" when testing locally without a CDN (SRI requires assets to be served with matching headers).

Your editor element

The loader calls ReadyEditor.init() using data-selector. Provide at least one matching element:

<div class="my-editor">
  <p>Start typing…</p>
</div>

Cross-origin embedding

ReadyEditor is designed for cross-origin use. Your host page (e.g. https://app.yourcompany.com) and the ReadyEditor CDN/loader can be on different origins.

How it works:

  1. Your page loads the loader script from the CDN.
  2. The loader resolves data-init-endpoint="/api/v1/editor/init" against the loader's origin (the CDN), not your page's origin.
  3. The init endpoint returns CORS headers allowing your page's origin (provided your domain is in the project's allowed domains list).

If you see domain_not_allowed errors, check that:

  • Your page's full domain (e.g. app.yourcompany.com) is added in the dashboard.
  • data-init-endpoint is a root-relative path (starts with /), not an absolute URL pointing to your page origin.

Toolbar presets

Control which plugins are shown using the loader:

Attribute Values Description
data-toolbar-preset full (default), basic, minimal Quick preset
data-plugins auto, none, comma list Override which plugins to load
data-disable-plugins comma list Remove specific plugins from the final set
data-toolbar-groups comma list Allowlist toolbar groups
data-toolbar-hide-groups comma list Blocklist toolbar groups
data-toolbar-overflow-groups comma list Groups always pinned into the "More" dropdown
data-toolbar-overflow on (default), off Set to off to disable the overflow menu entirely (toolbar wraps instead)

Toolbar group names: Formatting, Insert, Tools, View, Other

Example — load all entitled plugins but hide the Bootstrap and emoji ones:

<script
  src="https://cdn.readyeditor.io/releases/<version>/js/readyeditor.loader.min.js"
  data-api-key="rk_..."
  data-selector=".my-editor"
  data-load-css="1"
  data-plugins="auto"
  data-disable-plugins="bootstrap,tools_emoji"
  data-require-sri="1"
></script>

Saving content

ReadyEditor edits HTML in-place inside the contenteditable element.

Read HTML:

var html = ReadyEditor.getInstance('.my-editor').getContent();

Set HTML:

ReadyEditor.getInstance('.my-editor').setContent('<p>New content</p>');

Textarea bridge — for standard HTML form submissions, load the bridge script separately (it is not auto-loaded by the loader) and call attachTextareas() after the loader resolves:

<!-- loader with data-auto-init="0" -->
<script
  src="https://cdn.readyeditor.io/releases/<version>/js/readyeditor.loader.min.js"
  data-api-key="rk_..."
  data-load-css="1"
  data-plugins="auto"
  data-auto-init="0"
></script>

<!-- bridge — must be loaded before calling attachTextareas() -->
<script src="https://cdn.readyeditor.io/releases/<version>/js/readyeditor.textarea.bridge.min.js"></script>

<script>
  window.ReadyEditorLoader.init({ apiKey: 'rk_...', loadCss: true, plugins: 'auto' })
    .then(function() {
      ReadyEditor.attachTextareas({
        selector: 'textarea.js-editor',
        editorOptions: { apiKey: 'rk_...' }
      });
    });
</script>

The bridge is included in every release as js/readyeditor.textarea.bridge.min.js. It hides the <textarea>, inserts an editor <div> in its place, and syncs content back automatically on form submit. See setup-usage.md for the full walkthrough.


Uploading images

ReadyEditor does not upload files by itself. The media plugin exposes an integration hook:

window.ReadyEditorUploadImage = async function(file, context) {
  // Upload to your backend and return the final public URL
  var formData = new FormData();
  formData.append('image', file);

  var response = await fetch('/api/upload-image', {
    method: 'POST',
    body: formData,
  });
  var result = await response.json();

  // Return a URL string, or an object with { url, alt }
  return result.url;
};

Once the function is defined, the "Upload image" toolbar button in the media plugin will use it.


Sanitization

Client-side sanitization is defense-in-depth only. The editor's setContent() includes a lightweight HTML allowlist sanitizer, but you must sanitize content on your server before persisting or rendering it back to end-users.

You can control client-side sanitization via ReadyEditor.init():

// Disable built-in sanitizer (use only if you handle it yourself)
ReadyEditor.init({
  selector: '.my-editor',
  apiKey: 'YOUR_API_KEY',
  sanitize: 'none',
});

// Provide a custom sanitizer
ReadyEditor.init({
  selector: '.my-editor',
  apiKey: 'YOUR_API_KEY',
  sanitizeHtml: function(html, context) {
    return DOMPurify.sanitize(html);
  },
});

This only affects setContent() — it does not replace server-side sanitization.