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

@charisol/plexo-sdk

v1.0.31

Published

Plexo visual email and landing page builder SDK — drag-and-drop template editor with AI integration.

Readme

@charisol/plexo-sdk

Visual drag-and-drop email & landing page builder SDK with AI integration — built for React.

npm version license


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-sdk

Peer dependencies (install if not already present):

npm install react react-dom

Quick 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: BASICMEDIUMHIGH. 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 clientLoader or use <ClientOnly> wrapper to ensure it's never executed on the server.


Changelog

Unreleased

  • Breaking: Removed the aiApiKey prop. 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.aiConfig no longer accepts apiKey/provider — only useAi/tier remain, for the same reason (a template's JSON is loaded client-side, so it can't hold a secret safely).
  • aiProvider is 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