Appearance
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