Skip to content

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