@charisol/plexo-sdk
v1.0.31
Published
Plexo visual email and landing page builder SDK — drag-and-drop template editor with AI integration.
Maintainers
Readme
@charisol/plexo-sdk
Visual drag-and-drop email & landing page builder SDK with AI integration — built for React.
Features
- 🎨 Visual drag-and-drop builder — rows, columns, and elements with live editing
- 📧 Email & Landing Page modes — export clean, production-ready HTML for both
- 🤖 AI content generation — edit a single element, edit the whole layout, or generate a fresh layout from a prompt, with support for OpenAI, Gemini, and Claude
- 🔒 Server-proxied AI, no exposed keys — your provider API key is never sent to or stored in the browser; every AI request is authenticated with your Plexo API key and resolved against your account's server-side configuration
- 💬 Claude-Code-style AI feedback — a loading bubble followed by a one-line summary of what changed, for both the per-element editor and the layout prompt bar
- 🖼️ Stock image search — Unsplash, Pexels, Pixabay integrations
- 🎨 Design token system — Strata token support for consistent brand theming
- 📦 ESM-only, pure client-side component — nothing to self-host; the SDK talks directly to Plexo's hosted API for AI/publish/compile using your Plexo API key
Installation
npm install @charisol/plexo-sdkPeer dependencies (install if not already present):
npm install react react-domQuick Start
Plain React (Vite / CRA)
import { useRef } from 'react';
import { PlexoBuilder, type PlexoBuilderRef } from '@charisol/plexo-sdk';
// Import the builder's stylesheet
import '@charisol/plexo-sdk/dist/plexo-sdk.css';
export function Editor() {
const builderRef = useRef<PlexoBuilderRef>(null);
const handleSave = async () => {
const result = await builderRef.current?.exportDesign('email');
console.log('HTML:', result?.html);
console.log('JSON:', result?.json);
};
return (
<div style={{ height: '100vh' }}>
<PlexoBuilder
ref={builderRef}
apiKey="your-api-key"
mode="email"
backgroundColor="#ffffff"
themeBgColor="#6d28d9"
themeFgColor="#ffffff"
textColor="#111827"
showSaveButton={false}
/>
<button onClick={handleSave}>Save</button>
</div>
);
}Next.js (App Router)
Important: The SDK is a purely client-side component that uses drag-and-drop and browser APIs. It must never be rendered on the server.
Step 1 — Create a dynamic wrapper
Because the SDK uses browser-only APIs, wrap it with next/dynamic and disable SSR. Be sure to import the CSS stylesheet:
// components/template-editor.tsx
'use client';
import dynamic from 'next/dynamic';
import type { PlexoBuilderRef } from '@charisol/plexo-sdk';
import { useRef } from 'react';
// Import the builder's stylesheet
import '@charisol/plexo-sdk/dist/plexo-sdk.css';
// Never SSR the builder — it relies on browser APIs (drag-and-drop, ResizeObserver, etc.)
const PlexoBuilder = dynamic(
() => import('@charisol/plexo-sdk').then((m) => ({ default: m.PlexoBuilder })),
{ ssr: false, loading: () => <p>Loading builder…</p> }
);
export function TemplateEditor() {
const builderRef = useRef<PlexoBuilderRef>(null);
const handleSave = async () => {
const result = await builderRef.current?.exportDesign('email');
console.log(result?.html);
};
return (
<div style={{ height: '100vh' }}>
<PlexoBuilder
ref={builderRef}
apiKey="your-api-key"
mode="email"
backgroundColor="#ffffff"
themeBgColor="#6d28d9"
themeFgColor="#ffffff"
textColor="#111827"
showSaveButton={false}
/>
<button onClick={handleSave}>Save</button>
</div>
);
}Step 2 — Use in a Server Component page
// app/editor/page.tsx (Server Component — no "use client")
import { TemplateEditor } from '@/components/template-editor';
export default function EditorPage() {
return <TemplateEditor />;
}Props Reference
PlexoBuilder
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| mode | 'email' \| 'landing_page' | — | Required. Output mode for the editor |
| apiKey | string | — | Required. Your Plexo API key |
| initialTemplate | TemplateJSON | — | Pre-load a saved template JSON |
| initialHtml | string | — | Pre-load raw HTML (landing page mode) |
| initialCss | string | — | Pre-load CSS (landing page mode) |
| initialJs | string | — | Pre-load JS (landing page mode) |
| showSaveButton | boolean | true | Show/hide the built-in save button |
| onSave | (data: { json, html }) => void | — | Callback when built-in save button is clicked |
| autoSave | boolean | false | Auto-save periodically |
| autoSaveDuration | number | 3000 | Auto-save interval in ms |
| onUpload | (file: File) => Promise<string> \| string | — | Custom image upload handler, returns URL |
| backgroundColor | string | '#0e1526' | Canvas background colour |
| themeBgColor | string | '#4f46e5' | Primary brand/accent colour |
| themeFgColor | string | '#ffffff' | Foreground colour on brand elements |
| textColor | string | '#e2e8f0' | Default body text colour |
| useAi | boolean | false | Enable AI content generation. Also gates locally — if false, AI UI is hidden/disabled without a round trip |
| aiProvider | 'openai' \| 'gemini' \| 'claude' \| string | 'openai' | Display/label only. The provider actually used is resolved server-side from your Plexo account's configuration — this prop does not select or override it |
| aiTier | 'AUTO' \| 'BASIC' \| 'MEDIUM' \| 'HIGH' \| string | 'AUTO' | Requested model tier, sent with each AI request as an override of your account's default tier. AUTO lets the server pick BASIC/MEDIUM/HIGH per-request based on prompt complexity |
| unsplashKey | string | — | Unsplash API key for stock images |
| pexelsKey | string | — | Pexels API key for stock images |
| pixabayKey | string | — | Pixabay API key for stock images |
AI Configuration
AI generation (per-element editing, whole-layout editing, and generating a brand-new layout when the canvas is empty) runs entirely server-side. There is no aiApiKey prop and never will be — a provider API key (OpenAI, Anthropic, or Gemini) is configured once against your Plexo account and stays there, encrypted at rest. The SDK authenticates AI requests with the same apiKey you already pass to PlexoBuilder; the server resolves your account's provider, key, and default tier from that.
<PlexoBuilder
apiKey="your-plexo-api-key"
useAi
aiTier="AUTO" // optional override: 'AUTO' | 'BASIC' | 'MEDIUM' | 'HIGH'
// no aiApiKey, no aiProvider selection — both are account-level, not props
...
/>Tiers, from cheapest/fastest to most capable: BASIC → MEDIUM → HIGH. AUTO (the default) classifies each prompt's complexity server-side and picks the right tier automatically — a quick "change the button color" won't spend the same budget as "redesign this whole landing page."
Per-template override: TemplateJSON.body.aiConfig can override useAi/tier for a single template:
aiConfig?: {
useAi?: boolean;
tier?: string;
}Notice there's no apiKey/provider field here either — a template's JSON is loaded into the browser wholesale for editing, so a secret stored inside it could never actually be kept safe. The real key always comes from your account's server-side configuration, never from the template.
Where AI requests actually go: if apiKey is "workspace-internal" (the sentinel used when the SDK is rendered by Plexo's own dashboard), requests stay same-origin. For every real Plexo API key — i.e. any third-party integration — requests are routed automatically to Plexo's hosted production API. There's nothing to configure. If a request ever fails with a 404 here, it means this package version is out of date; update with npm install @charisol/plexo-sdk@latest.
PlexoBuilderRef — Imperative API
Access via ref on the component:
const builderRef = useRef<PlexoBuilderRef>(null);
// Export the current design
const { json, html } = await builderRef.current.exportDesign('email');
// or:
const { json, html } = await builderRef.current.exportDesign('landing_page');
// Get the JSON without compiling HTML
const json = builderRef.current.getJson();| Method | Signature | Description |
|--------|-----------|-------------|
| exportDesign | (target: 'email' \| 'landing_page') => Promise<{ json: TemplateJSON; html: string }> | Compile & export the current design |
| getJson | () => TemplateJSON | Get the raw design JSON snapshot |
Framework Notes
Vite
Works out of the box — no extra config needed.
Next.js (Pages Router)
Same dynamic import pattern applies:
import dynamic from 'next/dynamic';
const PlexoBuilder = dynamic(
() => import('@charisol/plexo-sdk').then((m) => ({ default: m.PlexoBuilder })),
{ ssr: false }
);Remix / React Router v7
Use the lazy approach or a client-only guard:
import { lazy, Suspense } from 'react';
const PlexoBuilder = lazy(() =>
import('@charisol/plexo-sdk').then((m) => ({ default: m.PlexoBuilder }))
);
// In your component:
<Suspense fallback={<p>Loading…</p>}>
<PlexoBuilder ... />
</Suspense>Note: In Remix, place this inside a
clientLoaderor use<ClientOnly>wrapper to ensure it's never executed on the server.
Changelog
Unreleased
- Breaking: Removed the
aiApiKeyprop. AI generation now runs server-side — your provider key is configured once against your Plexo account (encrypted at rest) instead of being passed to the browser on every render. - Breaking:
TemplateJSON.body.aiConfigno longer acceptsapiKey/provider— onlyuseAi/tierremain, for the same reason (a template's JSON is loaded client-side, so it can't hold a secret safely). aiProvideris now display/label-only; the effective provider comes from your account configuration, not this prop.- New Claude-Code-style AI feedback: a loading bubble followed by a one-line summary of what changed, for both the per-element "Edit with AI" popup and the layout AI prompt bar.
- The layout AI prompt bar now generates a brand-new layout automatically when the canvas is empty, instead of only editing existing content.
- AI requests (and publish/validate-key requests, when using a real Plexo API key rather than the dashboard's internal session) now route automatically to Plexo's hosted production API — nothing to configure. A 404 here means the installed package version is stale; update to the latest.
1.0.1
- Fixed: Removed Rolldown CJS interop shim (
require()) from ESM output — resolves compatibility issues with Turbopack (Next.js 15+/16+) and strict ESM environments - All React sub-paths (
react/jsx-runtime, etc.) are now properly externalized
1.0.0
- Initial release
License
MIT © Plexo HQ
