Skip to content
This plugin is new and currently in beta. For the stable version, please use the previous version of the plugin.

Getting Started

Installation

Prerequisites

Before you begin, ensure you have:

  • Node.js v22.x or higher installed on your system
  • npm or yarn package manager
  • A text editor (VS Code recommended for the best development experience)
  • Basic JavaScript knowledge and familiarity with modern web development

Step 1: Set Up Your Project

Create a new directory for your extension project and initialize it:

bash
mkdir extension-starter
cd extension-starter
npm init -y

Expected result: A new package.json file will be created in your directory.

Step 2: Install the Extensions SDK

Install the Stripo Editor Extensions SDK:

bash
npm install @stripoinc/ui-editor-extensions

For development, you will also need a bundler. We recommend using Vite:

bash
npm install --save-dev vite

Expected result: The packages will be installed in the node_modules directory and listed in your package.json dependencies.

Step 3: Create Your Project Structure

Create the following file structure:

bash
extension-starter/
├── index.html
├── src/
   ├── creds.js
   ├── index.js
   └── extension.js
├── package.json
└── vite.config.js

Create each file with the following content:

Create index.html:

html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Stripo Plugin</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        html, body {
            min-width: 1200px;
            height: 100%;
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
        }

        #stripoEditorContainer {
            height: calc(100% - 103px); /* 65px header + 38px disclaimer */
            background-color: #f5f5f7;
        }

        #externalSystemContainer {
            background: linear-gradient(to right, #ffffff, #fafafa);
            padding: 12px 24px;
            display: flex;
            justify-content: space-between;
            align-items: center;
            box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
            border-bottom: 1px solid #e1e1e1;
            height: 65px;
        }

        #externalSystemContainer > div:first-child {
            font-size: 18px;
            font-weight: 600;
            color: #1a1a1a;
            letter-spacing: -0.02em;
        }

        .control-button {
            background: #ffffff;
            border: 1px solid #e1e1e1;
            border-radius: 8px;
            padding: 8px 16px;
            font-size: 14px;
            font-weight: 500;
            color: #4a4a4a;
            cursor: pointer;
            transition: all 0.2s ease;
        }

        .control-button:hover {
            background: #f8f8f8;
            border-color: #c5c5c5;
            transform: translateY(-1px);
            box-shadow: 0 2px 4px rgba(0, 0, 0, 0.08);
        }

        .control-button:active {
            transform: translateY(0);
            box-shadow: none;
        }

        .control-button.active {
            background: #34c759;
            border-color: #34c759;
            color: white;
        }

        .control-button.active:hover {
            background: #2ca548;
            border-color: #2ca548;
        }

        .btn-group {
            display: flex;
            gap: 1px;
            background: #e1e1e1;
            border-radius: 8px;
            padding: 1px;
        }

        .btn-group button {
            background: #ffffff;
            border: none;
            padding: 8px 16px;
            font-size: 14px;
            font-weight: 500;
            color: #6b6b6b;
            cursor: pointer;
            transition: all 0.2s ease;
        }

        .btn-group button:first-child {
            border-radius: 7px 0 0 7px;
        }

        .btn-group button:last-child {
            border-radius: 0 7px 7px 0;
        }

        .btn-group button:hover {
            background: #f8f8f8;
        }

        .btn-group button.active {
            background: #34c759;
            color: white;
        }

        .btn-group button.active:hover {
            background: #2ca548;
        }

        .avatar-preview {
            width: 34px;
            height: 34px;
            border-radius: 15px;
            background-size: cover;
            display: inline-block;
            margin-left: 5px;
        }

        /* Notifications styles */
        .notification-zone {
            position: fixed;
            width: 700px;
            max-width: 90%;
            z-index: 99999;
            right: 30px;
            bottom: 80px;
        }

        .alert-success {
            color: #0f5d0f;
            background: #e6f7e6;
            padding: 16px 20px;
            border-left: 4px solid #28a745;
            border-radius: 8px;
            box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
        }

        .alert-info {
            color: #004085;
            background: #e7f3ff;
            padding: 16px 20px;
            border-left: 4px solid #007bff;
            border-radius: 8px;
            box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
        }
    </style>
</head>
<body>
<script>
    document.addEventListener('DOMContentLoaded', function() {
        const desktopButton = document.getElementById('desktopViewButton');
        const mobileButton = document.getElementById('mobileViewButton');
        const codeEditorButton = document.getElementById('codeEditor');
        const historyButton = document.getElementById('versionHistoryButton');

        function setActiveButton(activeButton, inactiveButton) {
            activeButton.classList.add('active');
            inactiveButton.classList.remove('active');
        }

        desktopButton.addEventListener('click', function() {
            setActiveButton(desktopButton, mobileButton);
        });

        mobileButton.addEventListener('click', function() {
            setActiveButton(mobileButton, desktopButton);
        });

        // Code Editor button toggle
        codeEditorButton.addEventListener('click', function() {
            codeEditorButton.classList.toggle('active');
        });

        // History button toggle
        historyButton.addEventListener('click', function() {
            historyButton.classList.toggle('active');
        });
    });
</script>

<div id="externalSystemContainer">
    <div style="padding-top: 5px; color: #6B6B6B">
        Stripo Plugin
    </div>

    <div class="btn-group">
        <button id="undoButton">Undo</button>
        <button id="versionHistoryButton">History</button>
        <button id="redoButton">Redo</button>
    </div>
    <div>
        <button id="codeEditor" class="control-button">Code editor</button>
    </div>
    <div class="btn-group viewSwitcher">
        <button id="desktopViewButton" class="active">Desktop</button>
        <button id="mobileViewButton">Mobile</button>
    </div>
</div>

<div style="background-color: #fef3c7; border-bottom: 1px solid #f59e0b; padding: 8px 24px; text-align: center;">
    <p style="margin: 0; font-size: 13px; color: #92400e; font-weight: 500;">
        <span style="font-weight: 700; color: #d97706;">⚠️ Please be advised:</span> The header shown above is not part of the plugin. It is intended solely for demonstration purposes and can be implemented independently in any desired way.
    </p>
</div>

<div id="no_plugin_id_info" class="notification-zone alert-info" style="display: none">
    <p>Please set PLUGIN_ID and SECRET_KEY to run the editor</p>
</div>

<div id="stripoEditorContainer"/>

<!-- Vite will replace this with the correct script dynamically -->
<script type="module" src="/src/index.js"></script>
</body>
</html>

Create vite.config.js:

javascript
import { defineConfig } from 'vite';

export default defineConfig({
    server: {
        port: 3000
    }
});

Create src/creds.js:

Important

Replace the placeholder values YOUR_PLUGIN_ID and YOUR_SECRET_KEY with your actual plugin credentials.

javascript
export const PLUGIN_ID = 'YOUR_PLUGIN_ID';
export const SECRET_KEY = 'YOUR_SECRET_KEY';

export const EDITOR_URL = 'https://plugins.stripo.email/resources/uieditor/latest/UIEditor.js';

export const USER_ID = '1';
export const EMAIL_ID = `${PLUGIN_ID}_${USER_ID}_1`;

Step 4: Create Your First Extension

Create src/extension.js:

javascript
import { ExtensionBuilder } from '@stripoinc/ui-editor-extensions';

// Create your first extension
const extension = new ExtensionBuilder()
    // Add custom styles to change the blocks panel background
    .addStyles(`
        .block-thumb {
            background-color: #33CC4D
        }
    `)
    .build();

export default extension;

What's happening here:

  • We import the ExtensionBuilder from the SDK
  • We create an extension using the builder pattern for clean, readable code
  • We add custom CSS styles that will be injected into the editor interface
  • We build and export the extension for use in the editor initialization

Step 5: Initialize the Editor

Create src/index.js:

javascript
import extension from './extension.js';
import { PLUGIN_ID, SECRET_KEY, EDITOR_URL, EMAIL_ID, USER_ID } from './creds';

// Load the Stripo editor script dynamically
function loadStripoEditor() {
    const script = document.createElement('script');
    script.id = 'UiEditorScript';
    script.src = EDITOR_URL;
    script.type = 'module';
    script.onload = _initializeEditor;
    document.head.appendChild(script);
}

// Initialize the editor with a demo template
function _initializeEditor() {
    _loadDemoTemplate(template => {
        _runEditor(template, extension);
    });
}

// Run the editor with the provided template and extension
function _runEditor(template, extension) {
    window.UIEditor.initEditor(
        document.querySelector('#stripoEditorContainer'),
        {
            html: template.html,
            css: template.css,
            metadata: {
                emailId: EMAIL_ID
            },
            locale: 'en',
            onTokenRefreshRequest: function (callback) {
                _request('POST', 'https://plugins.stripo.email/api/v1/auth',
                    JSON.stringify({
                        pluginId: PLUGIN_ID,
                        secretKey: SECRET_KEY,
                        userId: USER_ID,
                        role: 'user'
                    }),
                    function(data) {
                        callback(JSON.parse(data).token);
                    }
                );
            },
            codeEditorButtonSelector: '#codeEditor',
            undoButtonSelector: '#undoButton',
            redoButtonSelector: '#redoButton',
            versionHistoryButtonSelector: '#versionHistoryButton',
            mobileViewButtonSelector: '#mobileViewButton',
            desktopViewButtonSelector: '#desktopViewButton',
            extensions: [
                extension
            ]
        }
    );
}

// Helper function to make HTTP requests
function _request(method, url, data, callback) {
    const req = new XMLHttpRequest();
    req.onreadystatechange = function () {
        if (req.readyState === 4 && req.status === 200) {
            callback(req.responseText);
        } else if (req.readyState === 4 && req.status !== 200) {
            console.error('Cannot complete request. Please check that you have entered valid PLUGIN_ID and SECRET_KEY values');
        }
    };
    req.open(method, url, true);
    if (method !== 'GET') {
        req.setRequestHeader('content-type', 'application/json');
    }
    req.send(data);
}

// Load a demo template from GitHub
function _loadDemoTemplate(callback) {
    _request('GET', 'https://raw.githubusercontent.com/ardas/stripo-plugin/master/Public-Templates/Basic-Templates/Trigger%20newsletter%20mockup/Trigger%20newsletter%20mockup.html', null, function(html) {
        _request('GET', 'https://raw.githubusercontent.com/ardas/stripo-plugin/master/Public-Templates/Basic-Templates/Trigger%20newsletter%20mockup/Trigger%20newsletter%20mockup.css', null, function(css) {
            callback({ html: html, css: css });
        });
    });
}

// Start loading when the page is ready
if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', loadStripoEditor);
} else {
    loadStripoEditor();
}

Step 6: Add npm Scripts

Update your package.json with development scripts:

json
{
  "scripts": {
    "dev": "vite",
    "build": "vite build"
  }
}

Step 7: Run Your Extension

Start the development server:

bash
npm run dev

Open your browser and navigate to http://localhost:3000.

Expected result:

  • The Stripo editor loads successfully with your custom styles applied
  • The blocks panel displays thumbnails with your custom background color
  • Your extension is properly integrated and functioning

What You've Accomplished

Congratulations! You've successfully:

Set up a development environment for Stripo extensions
Created your first extension using ExtensionBuilder
Added custom styles to the editor interface
Initialized the editor with your custom extension
Verified your changes are reflected in real-time

Common Issues and Solutions

Issue: Editor Does Not Load

Possible solutions:

  • Ensure the Stripo script URL is accessible and not blocked by network policies
  • Check the browser console for any JavaScript errors that might prevent loading
  • Verify that your plugin credentials are correct and valid

Issue: Styles Do Not Appear

Possible solutions:

  • Check for CSS syntax errors in your extension styles
  • Ensure the extension is properly registered in the editor configuration
  • Verify that CSS selectors are targeting the correct elements

Issue: Module Not Found Errors

Possible solutions:

  • Run npm install to ensure all dependencies are properly installed
  • Check that import paths are correct and match your file structure
  • Verify that the Stripo Extensions SDK is installed and up to date