@pexelize/react-editor
v2.1.10
Published
React wrapper component for the Pexelize Editor - thin wrapper that exposes raw SDK for maximum flexibility
Maintainers
Readme
@pexelize/react-editor
React wrapper for the Pexelize Editor - a drag-and-drop email and page builder.
Installation
npm install @pexelize/react-editorQuick Start
import { useRef, useState } from "react";
import {
PexelizeEditor,
PexelizeEditorRef,
DesignJson,
} from "@pexelize/react-editor";
function EmailBuilder() {
const editorRef = useRef<PexelizeEditorRef>(null);
const [lastSaved, setLastSaved] = useState<Date | null>(null);
const handleExport = async () => {
const editor = editorRef.current?.editor;
if (!editor) return;
const { html, design } = await editor.exportHtmlAsync();
console.log("HTML:", html);
};
const handleSave = async () => {
const editor = editorRef.current?.editor;
if (!editor) return;
const design = await editor.getDesign();
await saveToBackend(design);
setLastSaved(new Date());
};
return (
<div style={{ height: "100vh", display: "flex", flexDirection: "column" }}>
<div style={{ padding: 16, borderBottom: "1px solid #eee" }}>
<button onClick={handleExport}>Export HTML</button>
<button onClick={handleSave}>Save</button>
{lastSaved && <span>Last saved: {lastSaved.toLocaleTimeString()}</span>}
</div>
<PexelizeEditor
ref={editorRef}
editorId={123}
apiKey="your-api-key"
height="100%"
options={{ editorMode: "email" }}
onReady={(editor) => {
console.log("Editor ready!");
// Load initial design if available
// editorRef.current.editor.loadDesign(savedDesign);
}}
onLoad={() => console.log("Design loaded")}
onChange={(data) => {
console.log("Design changed:", data.type);
// Auto-save or mark as dirty
}}
onError={(error) => console.error("Editor error:", error)}
/>
</div>
);
}Complete Example with All Features
import { useRef, useState, useCallback } from "react";
import {
PexelizeEditor,
PexelizeEditorRef,
DesignJson,
MergeTag,
} from "@pexelize/react-editor";
function AdvancedEmailBuilder() {
const editorRef = useRef<PexelizeEditorRef>(null);
const [isDirty, setIsDirty] = useState(false);
// Merge tags for personalization
const mergeTags: MergeTag[] = [
{ name: "First Name", value: "{{first_name}}" },
{ name: "Last Name", value: "{{last_name}}" },
{ name: "Company", value: "{{company}}" },
];
const handleReady = useCallback((editor) => {
console.log("Editor initialized");
// Set merge tags
editor.setMergeTags(mergeTags);
// Set custom fonts
editor.setFonts({
showDefaultFonts: true,
customFonts: [{ label: "Brand Font", value: "BrandFont, sans-serif" }],
});
// Load saved design or start blank
const savedDesign = localStorage.getItem("email-design");
if (savedDesign) {
editor.loadDesign(JSON.parse(savedDesign));
}
}, []);
const handleChange = useCallback(
(data: { design: DesignJson; type: string }) => {
setIsDirty(true);
// Auto-save to localStorage
localStorage.setItem("email-design", JSON.stringify(data.design));
},
[],
);
const handleExportHtml = async () => {
const editor = editorRef.current?.editor;
if (!editor) return;
const { html, design } = await editor.exportHtmlAsync();
// Download HTML file
const blob = new Blob([html], { type: "text/html" });
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = "email.html";
a.click();
};
const handleExportImage = () => {
const editor = editorRef.current?.editor;
if (!editor) return;
editor.exportImage((data) => {
window.open(data.url, "_blank");
});
};
const handlePreview = () => {
const editor = editorRef.current?.editor;
editor?.showPreview("desktop");
};
const handleUndo = () => editorRef.current?.editor?.undo();
const handleRedo = () => editorRef.current?.editor?.redo();
return (
<div style={{ height: "100vh", display: "flex", flexDirection: "column" }}>
{/* Toolbar */}
<div
style={{
padding: 12,
borderBottom: "1px solid #ddd",
display: "flex",
gap: 8,
}}
>
<button onClick={handleUndo}>Undo</button>
<button onClick={handleRedo}>Redo</button>
<button onClick={handlePreview}>Preview</button>
<button onClick={handleExportHtml}>Export HTML</button>
<button onClick={handleExportImage}>Export Image</button>
{isDirty && <span style={{ color: "orange" }}>● Unsaved changes</span>}
</div>
{/* Editor */}
<PexelizeEditor
ref={editorRef}
editorId={123}
apiKey="your-api-key"
height="100%"
editorMode="email"
options={{
appearance: { theme: "light" },
features: {
preview: true,
undoRedo: true,
imageEditor: true,
},
}}
onReady={handleReady}
onChange={handleChange}
onError={(error) => alert(`Error: ${error.message}`)}
/>
</div>
);
}
export default AdvancedEmailBuilder;Props
| Prop | Type | Required | Description |
| ------------- | ------------------- | -------- | -------------------------------------------------------- |
| editorId | number \| string | ✅ | Editor ID from your Pexelize dashboard |
| apiKey | string | ✅ | API key from your Pexelize dashboard |
| templateId | string | ❌ | Template ID for AI Copilot history |
| design | DesignJson | ❌ | Initial design to load |
| editorMode | EditorMode | ❌ | 'email' | 'web' | 'popup' |
| contentType | EditorContentType | ❌ | 'page' | 'module' |
| ai | AIConfig | ❌ | AI features configuration |
| height | string \| number | ❌ | Editor height (default: '600px') |
| options | PexelizeConfig | ❌ | SDK configuration options |
| className | string | ❌ | CSS class for the container |
| style | CSSProperties | ❌ | Inline styles for the container |
| onReady | (editor) => void | ❌ | Called when editor is ready |
| onLoad | () => void | ❌ | Called when design is loaded |
| onChange | (data) => void | ❌ | Called when design changes (receives { design, type }) |
| onError | (error) => void | ❌ | Called on initialization error |
SDK Methods Reference
Access the SDK via editorRef.current?.editor:
Design Methods
// Load/save design
editor.loadDesign(design);
editor.loadDesign(design, { preserveHistory: false });
await editor.loadDesignAsync(design);
editor.loadBlank();
editor.saveDesign((design) => console.log(design));
const design = await editor.getDesign();Export Methods
// HTML export
editor.exportHtml((data) => console.log(data.html, data.design));
const { html, design, chunks } = await editor.exportHtmlAsync();
// Plain text export
editor.exportPlainText((data) => console.log(data.text));
const { text, design } = await editor.exportPlainTextAsync();
// Image export
editor.exportImage((data) => console.log(data.url), { format: "png" });
// PDF export
editor.exportPdf((data) => console.log(data.url), { format: "A4" });
// ZIP export
editor.exportZip((data) => console.log(data.url));Merge Tags
editor.setMergeTags([
{ name: "First Name", value: "{{first_name}}" },
{ name: "Company", value: "{{company}}" },
]);
const tags = await editor.getMergeTags();Design Tags
editor.setDesignTags({ brand_color: "#007bff" });
const tags = await editor.getDesignTags();Special Links
editor.setSpecialLinks([{ name: "Unsubscribe", href: "{{unsubscribe_url}}" }]);
const links = await editor.getSpecialLinks();Modules
editor.setModules(modules);
const modules = await editor.getModules();
await editor.addModule(module);
await editor.removeModule(moduleId);
editor.updateModule(moduleId, rowData);
const syncedIds = await editor.getSyncedModulesInDesign();
await editor.unlinkSyncedModule(rowId);Fonts
editor.setFonts({
showDefaultFonts: true,
customFonts: [{ label: "Custom Font", value: "CustomFont" }],
});
const fonts = await editor.getFonts();Body Values
editor.setBodyValues({
backgroundColor: "#f5f5f5",
contentWidth: "600px",
});
const values = await editor.getBodyValues();Editor Options
editor.setOptions({
display: { backgroundColor: "#f5f5f5", contentWidth: 600 },
links: { color: "#0066cc", underline: true },
buttonDefaults: { backgroundColor: "#3AAEE0" },
appearance: { theme: "auto", accentColor: "indigo" },
});Tools Configuration
editor.setToolsConfig({
heading: { enabled: true },
button: { properties: { colors: { value: { backgroundColor: "#007bff" } } } },
});Tabs Management
editor.updateTabs({
myCustomTab: { enabled: true, position: 1, icon: "star", active: true },
});Editor Mode & Config
editor.setEditorMode("email"); // 'email' | 'web' | 'popup' | 'document'
editor.setEditorConfig({
minRows: 1,
maxRows: 1,
contentType: "module",
autoSelectOnDrop: true,
});
const config = await editor.getEditorConfig();Locale & Language
editor.setLocale("en-US");
editor.setCurrentLanguage("es-ES");
editor.setLanguages([
{ label: "English", value: "en-US", default: true },
{ label: "Español", value: "es-ES" },
]);
const lang = await editor.getCurrentLanguage();
editor.setTextDirection("rtl");Appearance
editor.setAppearance({
theme: "dark",
panels: { tools: { position: "left" } },
});Custom CSS/JS
editor.setCustomCSS(["https://example.com/styles.css"]);
editor.setCustomJS(["https://example.com/script.js"]);Preview
editor.showPreview("desktop"); // 'desktop' | 'tablet' | 'mobile'
editor.hidePreview();Undo/Redo
editor.undo();
editor.redo();
const canUndo = await editor.canUndo();
const canRedo = await editor.canRedo();Save
editor.save(); // Triggers save callbackElement Manipulation
editor.execCommand({
action: "highlight",
target: { type: "row", id: "row-123" },
});
editor.selectElement({ type: "row", id: "row-123" });
editor.highlightElement({ type: "content", id: "content-789" });
editor.highlightElement(null); // Clear highlight
editor.scrollToElement({ type: "row", id: "row-123" });Events
const unsubscribe = editor.addEventListener("design:updated", (data) => {
console.log("Design changed:", data);
});
editor.removeEventListener("design:updated", callback);Available Events:
editor:ready- Editor initializeddesign:loaded- Design loadeddesign:updated- Design changedelement:selected- Element selectedsave- Save triggeredpreview:shown/preview:hidden- Preview toggledimage:uploaded/image:error- Image upload events
Callbacks
editor.registerCallback("image", (file, done) => {
uploadToMyServer(file).then((url) => done({ url }));
});
editor.registerCallback("save", (data, done) => {
saveToBackend(data).then(() => done({ success: true }));
});
editor.registerCallback("linkClick", (data, done) => {
done({ url: data.url, target: "_blank" });
});
editor.unregisterCallback("image");
// Shorthand methods
editor.onSave(callback);
editor.offSave();
editor.onChange(callback);
editor.onLoad(callback);
editor.onModuleSave(callback);
editor.onSyncedModuleUpdate(callback);Tool Registration
await editor.registerTool({
id: "myCustomTool",
label: "My Tool",
baseToolType: "text",
icon: "star",
properties: { ... },
});
await editor.unregisterTool("myCustomTool");
const tools = await editor.getTools();State
const state = await editor.getState();Collaboration
editor.setCollaborationConfig({
enabled: true,
threadPreview: true,
user: { id: "user-123", name: "John Doe" },
});Utility Methods
editor.registerColumns([1, 2, 1]); // 1:2:1 layout
editor.setColorPickerConfig({
colors: ["#FF0000", "#00FF00", "#0000FF"],
recentColors: true,
});
editor.setStyleGuide({
tools: {
heading: {
styles: {
h1: { label: "Heading 1", values: { fontSize: "32px" } },
},
},
},
});Validation & Audit
const result = await editor.validateTemplate(data, { strict: true, detailed: true });
editor.validateTemplateCallback(data, (result) => console.log(result));
const toolResult = await editor.validateTool(data, { toolType: "button" });
const toolTypes = await editor.getToolTypes();
editor.setValidator((info) => {
if (!info.html.includes("alt=")) {
return [{ id: "missing-alt", title: "Missing Alt Text", severity: "WARNING" }];
}
return [];
});
editor.setToolValidator("image", (info) => { ... });
editor.clearValidators();
const auditResult = await editor.audit();Display Conditions
editor.setDisplayConditions({
enabled: true,
conditions: [
{
type: "Customer Segment",
label: "VIP Members",
before: '{% if customer.tier == "VIP" %}',
after: "{% endif %}",
},
],
});Image Upload
const result = await editor.uploadImage(file, {
folderId: "folder-123",
altText: "Description",
onProgress: (progress) => console.log(`${progress.percent}%`),
});Asset Management
const { assets, total } = await editor.listAssets({
folderId: "folder-123",
page: 1,
});
const deleteResult = await editor.deleteAsset(assetId);
const folders = await editor.listAssetFolders(parentId);
const folder = await editor.createAssetFolder("New Folder", parentId);
const storageInfo = await editor.getStorageInfo();AI Image
const result = await editor.saveAiImage(base64Data, {
prompt: "A sunset over mountains",
style: "realism",
suggestedFilename: "ai-sunset.png",
});Storage & AI Utilities
editor.isExternalStorage();
editor.canSaveAiImages();
editor.getAssetStorageMode(); // 'pexelize' | 'external'
editor.isAIDisabled();
editor.isAIFeatureEnabledCheck("copilot");
editor.isAIFeatureExternalCheck("imageGeneration");
editor.getAIFeatureModeCheck("smartHeading"); // 'pexelize' | 'external' | 'disabled'
editor.getAIMode();Status
editor.isInitialized();
editor.isReady();
editor.destroy();Hook API
import { PexelizeEditor, usePexelizeEditor } from "@pexelize/react-editor";
function MyEditor() {
const { ref, editor, isReady, isLoading, error } = usePexelizeEditor();
return (
<div>
<button
onClick={() => editor?.exportHtml(console.log)}
disabled={!isReady}
>
Export
</button>
<PexelizeEditor ref={ref} editorId={123} apiKey="your-api-key" />
</div>
);
}TypeScript
All SDK types are re-exported:
import type {
PexelizeConfig,
DesignJson,
ExportHtmlData,
MergeTag,
EditorEventName,
AIConfig,
} from "@pexelize/react-editor";License
MIT
