---
url: https://plugin.stripo.email/extensions/getting-started.md
---
# 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`:

:::warning 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](/extensions/reference/core/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
