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

@mark_stagepanda/email-studio-dropin

v0.1.5

Published

Drop-in React TypeScript email studio builder

Readme

@mark_stagepanda/email-studio-dropin

A drop-in, TypeScript-ready React email studio that can be embedded in Stagepanda or any React app.

Current package version: 0.1.2

What it provides

  • Drag/drop email block editor UI
  • Editable HTML view (paste/edit HTML and apply it to canvas)
  • Image upload + on-canvas resize/rotate handles
  • Right-side block settings panel
  • Mobile/desktop preview modes
  • Undo/redo history (20 activities) with version dropdown restore
  • File-based starter templates from template-library/ folders
  • Typed onStateChange callback for persistence

Install

Option A: local package (recommended during development)

From this repo root:

cd packages/email-studio-dropin
npm install
npm run build
npm pack

Then in your target React app (for example, Stagepanda):

npm install /absolute/path/to/packages/email-studio-dropin/<generated-tarball>.tgz

Option B: publish to npm (for third-party users)

cd packages/email-studio-dropin
npm publish --access public

Then consumers install with:

npm install @mark_stagepanda/email-studio-dropin
# or pin this release explicitly:
npm install @mark_stagepanda/[email protected]

Usage

import React, { useState } from "react";
import {
  EmailStudioDropin,
  EmailStudioOutput,
  EmailStudioState,
  exportEmailHtmlFromState,
} from "@mark_stagepanda/email-studio-dropin";

export default function EmailBuilderPage() {
  const [editorState, setEditorState] = useState<EmailStudioState | null>(null);
  const [currentHtml, setCurrentHtml] = useState("");
  const [output, setOutput] = useState<EmailStudioOutput | null>(null);

  return (
    <div style={{ height: "calc(100vh - 64px)" }}>
      <EmailStudioDropin
        height="100%"
        initialHtml="<!doctype html><html><body>...</body></html>"
        defaultTemplateId="library:invitations/event-invitation"
        onStateChange={(state) => setEditorState(state)}
        onHtmlChange={(html) => setCurrentHtml(html)}
        onOutputChange={(next) => setOutput(next)}
        bootstrap={{
          companyName: "Stagepanda",
          socialLinks: {
            facebook: "https://facebook.com/stagepanda",
            linkedin: "https://linkedin.com/company/stagepanda",
            instagram: "https://instagram.com/stagepanda",
            youtube: "https://youtube.com/@stagepanda",
            stagepanda: "https://stagepanda.com",
          },
        }}
      />
    </div>
  );
}

Exporting for email clients

The editor top bar includes built-in export controls with presets for Universal, Gmail, and Outlook.

You can also export programmatically:

const html = exportEmailHtmlFromState(editorState, { client: "outlook" });

Programmatic export helper:

  • exportEmailHtmlFromState(state, { client?: "universal" | "gmail" | "outlook", width?: number })
  • downloadEmailHtml(state, { client?: "universal" | "gmail" | "outlook", filename?: string, width?: number })
  • importHtmlToBlocks(html) to map pasted HTML into editable design blocks

Props

  • initialState?: Partial<EmailStudioState> Seed initial editor data (name, blocks, preview, etc).
  • initialHtml?: string Optional email HTML string to import as editable blocks on load. If initialState.blocks is provided, it takes precedence over initialHtml.
  • defaultTemplateId?: string Sets the starter template when initialState.blocks is empty. Use a generated ID from template-library (for example library:invitations/event-invitation).
  • onStateChange?: (state: EmailStudioState) => void Called whenever editor state changes.
  • onHtmlChange?: (html: string) => void Called whenever editor state changes with the latest generated email HTML.
  • onOutputChange?: (output: { json: EmailStudioState; html: string; subject: string; preheader: string }) => void Called whenever editor state changes with JSON, HTML, plus email subject and preheader.
  • htmlClient?: "universal" | "gmail" | "outlook" Chooses the HTML generation preset used by onHtmlChange.
  • className?: string Class name on root container.
  • style?: React.CSSProperties Additional inline root styles.
  • hideTopBar?: boolean Hide the top action bar if embedding into an existing shell.
  • height?: number | string Root editor height ("100vh" by default).
  • fontHref?: string Override the loaded webfont URL.
  • bootstrap?: { companyName?: string; socialLinks?: Partial<Record<"facebook" | "twitter" | "x" | "linkedin" | "instagram" | "youtube" | "stagepanda", string>> } Optional initializer. Company name is auto-applied to footer block content; social links are auto-applied to matching social icons.

Templates

  • Template selection is opened from Create campaign + and shown in a bottom-sheet selector.
  • The selector shows categories on the left and template thumbnails on the right.
  • Selecting a template replaces the current canvas blocks.
  • Folder-generated templates are exported from package:
    • TEMPLATE_PRESETS
    • DEFAULT_TEMPLATE_ID
    • createTemplateBlocks(templateId, bootstrap?)

File-Based Template Categories (Primary Folder)

You can manage templates from files using:

packages/email-studio-dropin/template-library/

Rules:

  • Every subfolder is a category.
  • Every .html file in that subfolder is a template.
  • Template name = filename without .html.

Example:

template-library/
  event-invitation/
    Launch-Invite.html
  reminders/
    Last-Call.html

After adding files, run:

npm run build --prefix packages/email-studio-dropin

The build auto-generates src/emailStudio/generatedTemplateLibrary.ts and those templates appear in the Create Campaign template selector by category.

Development hot reload:

npm start

This now runs watchers for template-library and drop-in TypeScript build, so edits/new HTML files are regenerated automatically during development.

Integration notes for any email project

  • Persist onStateChange output in your backend (JSON).
  • Restore saved drafts via initialState.
  • Wrap the component in a fixed-height container (height: 100% or viewport-calculated) to avoid layout collapse.
  • Keep react and react-dom as host app dependencies (peer deps).

Stagepanda-specific integration checklist

  1. Create a route/page for the editor and mount EmailStudioDropin in a full-height container.
  2. Connect onStateChange to your draft autosave endpoint.
  3. Optionally pass hideTopBar if Stagepanda already has global actions.
  4. Persist and rehydrate initialState for editing existing templates.

Block Extension Workflow

When adding a new content block type, update these extension points:

  1. src/emailStudio/constants.ts
  • Add the block to BLOCKS (left panel + insert menu).
  • Add default config in DEFAULTS.
  1. src/emailStudio/components/icons.tsx
  • Add BlockIcon rendering for the new type.
  1. src/EmailStudioDropin.tsx
  • Ensure actions.addBlock maps/normalizes the block type if needed.
  • Add any bootstrap-driven defaults.
  1. src/emailStudio/components/Canvas.tsx
  • Add render branch in renderBlockContent for canvas preview/editing.
  1. src/emailStudio/components/RightPanel.tsx
  • Add settings panel UI branch for the block.
  • Keep settings updates isolated where possible (small subcomponents per block).
  1. src/emailStudio/exportHtml.ts
  • Add HTML export branch for the new block.
  1. src/emailStudio/importHtml.ts (optional)
  • Add reverse mapping if HTML import should produce this block.
  1. Regression coverage
  • Add/update tests for:
    • insert behavior
    • settings behavior
    • export output
    • any bootstrap/default interactions

Distribution Verification

Before publishing, run:

npm run verify:dropin

This runs clean build and package dry-run checks for the drop-in.