MiN8T
Home

Embed MiN8T's Email Editor in Your Product: A Step-by-Step Guide

Sarah Chen
Sarah Chen
Email Strategy Lead at MiN8T
7
Steps to embed
10
Lines of code to start
Any
Framework supported
100%
White-label ready

1 Overview

The MiN8T Plugin SDK lets you embed a full drag-and-drop email editor directly inside your own application. Your users design emails without ever leaving your product -- no redirects, no iframe hacks, no "powered by" badges unless you want them.

This is how SaaS platforms add email design to their feature set without building an editor from scratch. Marketing automation tools, CRM platforms, agency dashboards, e-commerce backends, and enterprise communication suites all use embedded editors to give their users a native email creation experience.

With the Plugin SDK, you get the full MiN8T editing experience -- drag-and-drop blocks, responsive design controls, image management, HTML and AMP output -- rendered inside a container element you control. You decide where the editor appears, what tools are available, which templates users can access, and how the final HTML gets saved.

i

Not an iframe. The Plugin SDK renders the editor natively in your DOM. This means full control over sizing, styling, and interaction. The editor adapts to your layout, not the other way around.


2 What You'll Need

Before you start, make sure you have these three things ready:

!

Keep your API Token secret. The token should never appear in client-side code that ships to browsers. Generate short-lived session tokens on your server and pass those to the frontend. We cover this pattern in the Going Further section.


3 Step 1: Create a Project

Every embedded editor instance belongs to a project. A project ties together your credentials, allowed origins, and usage quotas.

  1. Log in to your MiN8T dashboard
  2. Navigate to Settings → Developer Tools → Projects
  3. Click Create Project
  4. Enter a name for your project (e.g., "My SaaS Email Editor")
  5. Add your allowed origins -- the domains where the editor will load (e.g., https://yourapp.com, http://localhost:3000 for development)
  6. Click Create

After creation, MiN8T displays your Project ID and API Token. Copy both and store them securely. The API Token is shown once -- if you lose it, you can regenerate it from the project settings, but the old token will stop working immediately.

Separate projects for environments. Create one project for development (with http://localhost origins) and another for production. This keeps your production token isolated and lets you test without affecting live usage quotas.


4 Step 2: Install the SDK

You can install the SDK via npm or load it from the CDN. Choose whichever fits your build process.

Option A: npm

Shell
npm install @min8t.com/plugin-sdk

Option B: CDN script tag

Add this to your HTML, ideally just before the closing </body> tag:

HTML
<script src="https://cdn.min8t.com/plugin-sdk/v1/min8t-sdk.js"></script>

The CDN version exposes a global Min8t object on window. The npm package provides named exports. Both give you the same API -- the only difference is how you import it.


5 Step 3: Initialize the Editor

Create a container element in your HTML, then call Min8t.init() to mount the editor inside it. Here is the minimal working example:

HTML
<!-- Container where the editor will render -->
<div id="min8t-editor" style="width: 100%; height: 800px;"></div>
JavaScript
import { Min8t } from '@min8t.com/plugin-sdk';

const editor = await Min8t.init({
  projectId: 'proj_abc123def456',
  token: 'YOUR_SESSION_TOKEN',
  container: '#min8t-plugin',
  locale: 'en',
});

// The editor is now live and ready for user interaction.

That is it -- ten lines including the container div. The init() call returns a promise that resolves to an editor instance. The editor loads inside your container, respecting whatever dimensions you set via CSS.

i

Responsive sizing: Set the container to width: 100%; height: 100vh; for a full-page editor, or constrain it to a specific region of your layout. The editor adapts to any size above 320px wide.


6 Step 4: Load a Template

Once the editor is initialized, you can load a blank canvas or an existing template. By default, the editor starts with a blank email. To load a specific template:

JavaScript
// Load a template by ID (from your MiN8T template library)
await min8t.setHtml('tpl_a1b2c3d4e5f6');

// Or load from raw HTML + JSON
await min8t.setHtml({
  html: '<html>...your email HTML...</html>',
  json: { /* MiN8T editor JSON structure */ }
});

// Start with a blank template explicitly
await min8t.setHtml('blank');

The loadTemplate() method accepts either a string template ID (which fetches the template from the MiN8T API) or an object with html and json properties (which loads the design directly without a network request). The JSON structure is the same format returned by min8t.compile(), so you can save it to your own database and reload it later.

Template library access: Templates loaded by ID must belong to the same account that owns the project. You can use this to offer your users a curated set of starter templates without exposing your full template library.


7 Step 5: Save and Export

When your user finishes editing, you need to extract the result. The editor provides two output formats: compiled HTML (ready to send) and JSON (the editor's internal format, for reloading later).

JavaScript
// Get the compiled HTML -- ready to send via any ESP
const html = await min8t.getHtml();

// Get the JSON design -- save this to reload the design later
const json = await min8t.compile();

// Save both to your backend
await fetch('/api/emails/save', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    emailId: currentEmailId,
    html: html,
    design: json,
  }),
});

The HTML output includes inlined CSS, responsive media queries, and is optimized for email client compatibility. The JSON output is a structured representation of the design that the editor can reload with full fidelity -- all block positions, styles, content, and settings are preserved.

!

Always save both. If you only save the HTML, you lose the ability to re-edit the design. The HTML is a one-way compiled output. Store the JSON alongside the HTML so users can pick up where they left off.


8 Step 6: Customize the Editor

The editor's appearance and available tools are fully configurable. Pass options to Min8t.init() to tailor the experience for your users.

JavaScript
const editor = await Min8t.init({
  projectId: 'proj_abc123def456',
  token: 'YOUR_SESSION_TOKEN',
  container: '#min8t-plugin',

  // Toolbar: show only the tools your users need
  toolbar: {
    blocks: true,       // Drag-and-drop content blocks
    images: true,       // Image upload and library
    structures: true,   // Layout structures (1-col, 2-col, etc.)
    settings: true,     // Email-wide settings (width, background)
    preview: true,      // Desktop/mobile preview toggle
    undo: true,         // Undo/redo buttons
    codeEditor: false,  // Hide the raw HTML editor
  },

  // Brand: lock colors and fonts to your client's brand
  brand: {
    colors: ['#0066FF', '#00CC88', '#FF6600', '#333333', '#FFFFFF'],
    fonts: ['Inter', 'Georgia', 'Arial'],
    defaultFont: 'Inter',
  },

  // Content blocks: add your own custom blocks
  customBlocks: [
    {
      id: 'product-card',
      label: 'Product Card',
      icon: 'https://yourapp.com/icons/product.svg',
      html: '<div class="product">...</div>',
    },
  ],

  // Locking: restrict what users can edit
  locked: {
    header: true,       // Lock the header block (no edits)
    footer: true,       // Lock the footer block
    deleteBlocks: false, // Allow deleting non-locked blocks
  },
});

The toolbar object toggles individual tools on and off. The brand object restricts the color picker and font dropdown to your approved palette -- users can still design freely, but every choice stays within brand guidelines. The locked object prevents modifications to specific regions, which is useful for enforcing consistent headers and footers across all emails.

Custom blocks are powerful. You can create product cards, pricing tables, social media footers, or any reusable component as a custom block. Users drag them from the sidebar just like built-in blocks, but the HTML is yours.


9 Step 7: Handle Events

The editor emits events as the user works. Hook into these to sync changes with your backend, trigger autosave, show notifications, or integrate with your app's state management.

JavaScript
// Fires every time the design changes (debounced, ~300ms)
min8t.on('change', async (event) => {
  console.log('Design modified:', event.timestamp);
  // Trigger autosave
  const json = await min8t.compile();
  saveDraft(json);
});

// Fires when the user clicks "Save" in the editor toolbar
min8t.on('save', async (event) => {
  const html = await min8t.getHtml();
  const json = await min8t.compile();
  await saveToBackend(html, json);
  showNotification('Email saved successfully');
});

// Fires when the user triggers an export
min8t.on('export', (event) => {
  console.log('Exported to:', event.target);
  console.log('HTML size:', event.htmlSize, 'bytes');
});

// Fires on errors (failed image upload, network timeout, etc.)
min8t.on('error', (event) => {
  console.error('Editor error:', event.code, event.message);
  showErrorToast(event.message);
});

// Fires when the editor finishes loading
min8t.on('ready', () => {
  hideLoadingSpinner();
});

Events follow a standard pattern: call min8t.on(eventName, callback) to subscribe and editor.off(eventName, callback) to unsubscribe. The change event is debounced internally at 300ms so you are not overwhelmed during rapid edits.

EventWhen It FiresPayload
readyEditor fully loaded and interactiveNone
changeAny design modification (debounced){ timestamp }
saveUser clicks Save{ timestamp }
exportExport completed{ target, htmlSize }
errorSomething went wrong{ code, message }
templateLoadedTemplate finished loading{ templateId }
destroyEditor instance destroyedNone

10 Going Further

Once you have the basics working, here are the next areas to explore.

White-labeling

On the Business and Enterprise plans, you can fully remove MiN8T branding from the editor. Pass whiteLabel: true in the init options. You can also inject your own logo into the editor toolbar using the logo option:

JavaScript
const editor = await Min8t.init({
  projectId: 'proj_abc123def456',
  token: 'YOUR_SESSION_TOKEN',
  container: '#min8t-plugin',
  whiteLabel: true,
  logo: 'https://yourapp.com/logo.svg',
});

Server-side token generation

Never expose your API Token in client-side code. Instead, create a token endpoint on your server that exchanges your API Token for a short-lived session token:

Node.js
// Server endpoint: POST /api/editor-token
app.post('/api/editor-token', authenticate, async (req, res) => {
  const response = await fetch('https://api.min8t.com/v1/plugin/token', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'ES-PLUGIN-AUTH': process.env.MIN8T_API_TOKEN,
    },
    body: JSON.stringify({
      projectId: process.env.MIN8T_PROJECT_ID,
      userId: req.user.id,       // Your app's user ID
      expiresIn: '1h',           // Token lifetime
    }),
  });

  const { sessionToken } = await response.json();
  res.json({ token: sessionToken });
});

// Client-side: fetch token before initializing
const { token } = await fetch('/api/editor-token', { method: 'POST' }).then(r => r.json());
const editor = await Min8t.init({
  projectId: 'proj_abc123def456',
  token: token,
  container: '#min8t-plugin',
});

Framework-specific examples

The SDK works the same way in every framework -- the only difference is where you call init() and destroy():

React
import { useEffect, useRef } from 'react';
import { Min8t } from '@min8t.com/plugin-sdk';

function EmailEditor({ token, templateId }) {
  const containerRef = useRef(null);

  useEffect(() => {
    let editor;

    async function initEditor() {
      editor = await Min8t.init({
        projectId: 'proj_abc123def456',
        token,
        container: containerRef.current,
      });
      if (templateId) await min8t.setHtml(templateId);
    }

    initEditor();
    return () => editor?.destroy();
  }, [token, templateId]);

  return <div ref={containerRef} style={{ width: '100%', height: '100vh' }} />;
}

Rate limits and quotas

SDK usage counts toward your plan's quotas. Here are the key limits to be aware of:

PlanActive SessionsAPI Calls / minStorage
Startup50 concurrent3005 GB
Business500 concurrent1,50050 GB
EnterpriseUnlimitedCustomCustom

If you exceed the concurrent session limit, new init() calls return an error with code QUOTA_EXCEEDED. Handle this gracefully in your error handler to show users a retry message.

Monitor usage: The Developer Tools dashboard shows real-time session counts, API call volume, and storage consumption. Set up alerts to get notified before you hit a limit.

Embed MiN8T in your product today

Get your SDK credentials and ship an email editor in under an hour.

Get started free