npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@pexelize/react-editor

v2.1.10

Published

React wrapper component for the Pexelize Editor - thin wrapper that exposes raw SDK for maximum flexibility

Readme

@pexelize/react-editor

React wrapper for the Pexelize Editor - a drag-and-drop email and page builder.

Installation

npm install @pexelize/react-editor

Quick 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 callback

Element 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 initialized
  • design:loaded - Design loaded
  • design:updated - Design changed
  • element:selected - Element selected
  • save - Save triggered
  • preview:shown / preview:hidden - Preview toggled
  • image: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