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

@lp-labs/react-email-builder

v1.0.0

Published

A standalone React email editor with typed blocks and table-based inline HTML export.

Readme

@lp-labs/react-email-builder

Package version: 1.0.0. Requires React ^18.2.0 || ^19.0.0 as its only runtime peer. Named ESM and CommonJS exports include declarations; TypeScript React consumers also need their application's React types.

A standalone React email editor for a deliberately small, typed block model. Its HTML export is built from presentation tables and inline styles; the only runtime dependency is React.

Install

npm install @lp-labs/react-email-builder react

Import the editor stylesheet once:

import "@lp-labs/react-email-builder/style.css";

Use

import { useState } from "react";
import { EmailBuilder, type EmailDocument } from "@lp-labs/react-email-builder";

const initial: EmailDocument = {
  version: 1,
  blocks: [
    { id: "welcome", type: "heading", level: 1, text: "Welcome" },
    { id: "body", type: "text", text: "Thanks for subscribing." },
    { id: "cta", type: "button", text: "Visit us", href: "https://example.com" }
  ]
};

export function EmailEditor() {
  const [email, setEmail] = useState(initial);
  return <EmailBuilder value={email} onChange={setEmail} />;
}

EmailBuilderProps (all fields optional):

| Prop | Type | Default | Description | | --- | --- | --- | --- | | value | EmailDocument | absent | Controlled document when supplied. | | defaultValue | EmailDocument | { version: 1, blocks: [] } | Initial document in uncontrolled mode. | | onChange | (document: EmailDocument) => void | absent | Called on each edit; controlled parents must store and pass back changes. | | className | string | absent | Added to the editor root for customization. |

Use either value or defaultValue. The editor provides palette insertion, selection, reordering, deletion, text/link/image/style inspection, undo/redo for up to 50 edit snapshots, desktop/tablet/mobile preview widths, JSON validation and serialization, and HTML export. The preview is a visual approximation and is not generated by embedding exported HTML.

Supported model and utilities

  • EmailDocument is { version: 1; blocks: EmailBlock[] }. Supported EmailBlock variants are text (text), heading (text, level 1-3), image (src, alt), button (text, href), divider, and spacer (height 0-1000 px). Blocks have unique, non-empty string IDs. Nesting, columns, arbitrary layout, and custom blocks are not supported.
  • Optional style fields are restricted to hex color/backgroundColor, padding (0-100 px), fontSize (8-48 px), and textAlign (left, center, right).
  • validateEmailDocument(unknown) returns path/message issues; empty means valid. parseEmailDocument(string) and serializeEmailDocument(EmailDocument) validate and throw an Error on malformed data. renderEmailHtml(EmailDocument) validates and exports a standalone HTML document. escapeHtml(string) is also exported.
  • URL checks trim outer whitespace and accept http:, https:, mailto:, tel:, and non-scheme relative paths. After trimming, empty URLs, remaining control characters, protocol-relative paths, and all other schemes (including javascript: and data:) are rejected. Text and attributes are escaped; the schema cannot carry arbitrary markup, JavaScript, event handlers, or free-form CSS.

Complete named exports

Runtime exports: EmailBuilder, validateEmailDocument, parseEmailDocument, serializeEmailDocument, renderEmailHtml, escapeHtml, and isSafeUrl. There is no default export. Type-only exports are EmailBuilderProps, EmailAlign, EmailBlockStyle, EmailTextBlock, EmailHeadingBlock, EmailImageBlock, EmailButtonBlock, EmailDividerBlock, EmailSpacerBlock, EmailBlock, EmailDocument, and ValidationIssue.

Every block variant contains required id: string, its literal type, and optional style: EmailBlockStyle. The additional required fields are:

| Exported variant | type | Additional fields | | --- | --- | --- | | EmailTextBlock | "text" | text: string | | EmailHeadingBlock | "heading" | text: string, level: 1 \| 2 \| 3 | | EmailImageBlock | "image" | src: string, alt: string | | EmailButtonBlock | "button" | text: string, href: string | | EmailDividerBlock | "divider" | None | | EmailSpacerBlock | "spacer" | height: number |

EmailBlock is their union. EmailAlign = "left" | "center" | "right". All EmailBlockStyle fields are optional: color?: string, backgroundColor?: string, padding?: number, fontSize?: number, and textAlign?: EmailAlign. Omitting style leaves renderer/browser defaults. ValidationIssue = { path: string; message: string }.

Helper signatures and behavior:

  • validateEmailDocument(value: unknown): ValidationIssue[] reports supported field/value violations without throwing for ordinary malformed JSON values.
  • parseEmailDocument(json: string): EmailDocument throws plain Error for malformed JSON or newline-separated validation issues.
  • serializeEmailDocument(document: EmailDocument): string validates and returns two-space-indented JSON; validation or native JSON serialization can throw.
  • renderEmailHtml(document: EmailDocument): string validates before rendering; invalid documents throw Error. No async rendering or cancellation is involved.
  • escapeHtml(value: string): string escapes &, <, >, double and single quotes. It is an HTML text/attribute escape helper, not a general sanitizer.
  • isSafeUrl(value: string): boolean trims for checking and implements the URL policy above; it does not normalize the returned document or assess trust, reachability, tracking, redirects, or email-client compatibility.

Standalone non-UI example:

import {
  parseEmailDocument, renderEmailHtml, serializeEmailDocument, isSafeUrl
} from "@lp-labs/react-email-builder";

const email = parseEmailDocument(JSON.stringify({
  version: 1,
  blocks: [{ id: "hello", type: "text", text: "Hello <reader>" }]
}));
console.log(serializeEmailDocument(email), renderEmailHtml(email));
console.log(isSafeUrl("https://example.com")); // true

Email rendering, accessibility, and limits

The renderer creates a fluid outer presentation table and a 600 px maximum-width inner table, uses table rows/cells for blocks, and emits inline styles rather than relying on an external stylesheet. It includes a basic viewport meta tag and semantic language/title metadata. This is not a claim of universal email-client rendering: clients vary in support for image sizing, CSS, links, fonts, and responsive behavior. Preview width modes do not emulate any particular mail client. Inspect and test exported messages in your target clients before use.

Controls use native labeled inputs, buttons, and selects. Preview headings, text, links, image alt text, separators, and spacer semantics are retained where appropriate. There is no drag/drop, rich-text formatting, asset upload, custom template engine, personalization/merge tags, email sending, or delivery/production guarantee. The JSON/HTML output fields are read-only; make edits with the palette and inspector.

Customize via className and the lp-email-* CSS classes, or render your own UI around the exported typed model.

Changes and helpers are synchronous. There is no autosave, subscription, worker, or cancellation API; retain state/persist it in the parent and discard it on teardown. Initial defaultValue is not a subsequent reset command. Undo history and full HTML/JSON generation copy/scan the document; there is no virtualization or block-count performance guarantee. External image URLs may load in the preview; the library does not upload assets or send email. Import alone starts no browser work. Use an application React renderer and DOM-capable browser for the interactive editor; styles affect the editor, not exported HTML.

Project

License

MIT.

Support

If this library is useful to you, consider supporting its maintenance with a donation via PayPal.

Author

Created by Libin Prasanth. Connect on LinkedIn for updates and discussion.