Back to Blogs
13 min readApr 21, 2026

React Email Editor: Build a Visual Email Builder Inside Your App

A complete guide to React Email Editor, the embeddable visual editor that lets users compose email templates with slash commands, bubble menus, multi-column layouts, and one-click HTML export. Learn how to integrate it in under 30 minutes.

react email editorreact email visual builderembeddable email editor reactemail template builder nextjstiptap email editorreact email tiptap prosemirror

React Email Editor: Build a Visual Email Builder Inside Your App

Most developers build transactional emails by writing JSX components in their editor, running a preview server, and calling render() to get HTML. That workflow is great for developers.

But what happens when your product needs non-developers marketers, designers, support leads to compose or edit email templates without touching code?

Until recently, the honest answer was: use a third-party email builder, export HTML, and hope it survives your email client.

React Email 1.0 changes that. It ships a purpose-built, embeddable visual editor that you can drop into your own product. Your users compose templates with a clean rich-text interface. Under the hood, it produces React Email components and email-ready HTML. No code required from the end user. Full control for you as the developer.

This guide covers how it works, how to set it up, and what you can build with it.

What the React Email Editor Actually Is

The React Email Editor is a visual composition tool built on top of TipTap and ProseMirror two battle-tested rich text editing frameworks that power tools like Notion, Linear, and dozens of other products.

What makes it different from a generic TipTap setup is that every extension in the editor is email-aware. Instead of serializing to standard HTML or JSON, the editor serializes to React Email components. The result is email-ready HTML that actually renders in Gmail, Outlook, Apple Mail, and mobile clients.

You embed the editor in your app. Your user types and clicks. You call an export function and get back production-ready email HTML. That is the whole flow.

Key Features You Get Out of the Box

Rich Text Editing

The editor handles everything you expect from a modern text editor:

  • Paragraphs, headings (H1–H4), bold, italic, underline, strikethrough
  • Ordered and unordered lists
  • Code blocks with syntax highlighting
  • Tables with full row and column control
  • Blockquotes and dividers

All of these serialize to email-safe HTML, not arbitrary CSS or modern browser features that will break in Outlook.

Bubble Menus

When a user selects text, a floating toolbar appears above the selection with relevant formatting options. This is the same interaction pattern used in Notion, Craft, and Linear.

You can customize which options appear in the bubble menu, add custom actions, or replace it entirely.

Slash Commands

Type / anywhere in the editor to open a command palette. Users can insert:

  • Text blocks, headings, lists
  • Images (via upload or URL)
  • Code blocks, tables
  • Dividers
  • Multi-column layouts

This removes the need for a toolbar at the top of the editor. The interface stays minimal until the user needs it.

Multi-Column Layouts

Two, three, and four-column layouts are supported out of the box. In normal HTML emails this is notoriously difficult to get right. But the editor handles the table-based layout structure that email clients require, so users just click "two columns" and it works.

Email Theming

The editor ships with built-in themes backed by CSS custom properties. You can:

  • Apply a theme from the provided set
  • Override individual CSS properties to match your brand
  • Pass a custom theme object to fully control the visual output

Themes affect both the editor appearance and the exported HTML, so what users see while composing closely matches what recipients see.

HTML Export

When a user finishes composing, you call the export function and get back:

  • Email-ready HTML (inline styles, table layouts, email client compatibility)
  • Plain text version for email clients that do not render HTML

This is where the React Email engine underneath does the heavy lifting. It is the same render pipeline that powers render() in the core library.

Custom Extensions

If you need something the editor does not ship with a custom block type, a branded component, a dynamic merge tag or a slash command, you can build it as a TipTap extension. The editor exposes the extension API and the email-aware serialization hooks so your custom nodes produce correct React Email output.

Architecture: Five Import Paths

The editor is organized into five entry points so you only import what you need:

Entry PointWhat It Contains
@react-email/editor/corecomposeReactEmail serializer, event bus, type definitions
@react-email/editor/extensionsStarterKit and 35+ email-aware extensions
@react-email/editor/uiBubble menus, slash command UI
@react-email/editor/pluginsEmail theming plugin, image upload plugin
@react-email/editor/utilsAttribute helpers, inline style utilities

This split keeps bundle size under control. If you only need the core editor without bubble menus, you skip @react-email/editor/ui. If you do not use image upload, you skip that plugin.

Setting Up the Editor

Install

pnpm add @react-email/editor

The editor requires react-email (version 6+) as a peer dependency. If you followed the React Email 6.0 migration, you already have it.

Basic Integration

"use client";import { useEditor, EditorContent } from "@react-email/editor/core";import { StarterKit } from "@react-email/editor/extensions";export function EmailEditor() {  const editor = useEditor({    extensions: [StarterKit],    content: "<p>Start typing your email here...</p>",  });  return (    <div className="border rounded-lg p-4">      <EditorContent editor={editor} />    </div>  );}

This gives you a working rich text editor in about ten lines of code. No toolbar, no slash commands yet — just a clean editable area.

Add the Full Feature Set

"use client";import { useEditor, EditorContent } from "@react-email/editor/core";import { StarterKit } from "@react-email/editor/extensions";import { BubbleMenu, SlashCommands } from "@react-email/editor/ui";import { EmailThemingPlugin } from "@react-email/editor/plugins";export function FullEmailEditor() {  const editor = useEditor({    extensions: [      StarterKit,      SlashCommands,    ],    plugins: [EmailThemingPlugin()],    content: "",  });  if (!editor) return null;  return (    <div className="email-editor-wrapper">      <BubbleMenu editor={editor} />      <EditorContent editor={editor} className="prose max-w-none" />    </div>  );}

Export to Email HTML

import { composeReactEmail } from "@react-email/editor/core";import { render } from "react-email";async function exportEmail(editor: Editor): Promise<string> {  // Serialize editor content to a React Email component tree  const emailComponent = composeReactEmail(editor);  // Render to email-ready HTML  const html = await render(emailComponent);  return html;}// Usageconst html = await exportEmail(editor);await resend.emails.send({  from: "team@yourapp.com",  to: recipient,  subject: "Your message",  html,});

The composeReactEmail function converts the editor's document state into React Email component JSX. You then pass that to render() as normal. The output is the same HTML you would get from writing the template by hand.

Real-World Use Cases

SaaS Products With Custom Email Templates

If your product sends emails on behalf of users, you might need to send notification emails, digest emails, outreach sequences, and more. The editor lets users customize those templates without leaving your app.

Instead of shipping a settings page with a textarea full of raw HTML, you embed the editor. Users click, drag, and format. You store the editor's JSON output and render it server-side when sending.

Marketing Tools and CRM

Email marketing features inside a SaaS product typically require a third-party integration (Mailchimp, SendGrid dynamic templates, etc.). With the React Email Editor, you can build that capability natively. Your users compose directly in your product and you own the rendering pipeline end to end.

Internal Tooling

Support, operations, and customer success teams often need to send templated emails — onboarding sequences, renewal notices, follow-up templates. Embedding the editor into an internal admin panel lets non-technical team members manage these templates themselves.

Adding an Image Upload Plugin

import { ImageUploadPlugin } from "@react-email/editor/plugins";const uploadPlugin = ImageUploadPlugin({  onUpload: async (file: File) => {    const formData = new FormData();    formData.append("file", file);    const response = await fetch("/api/upload", {      method: "POST",      body: formData,    });    const data = await response.json();    return data.url; // Return the public URL of the uploaded image  },});const editor = useEditor({  extensions: [StarterKit],  plugins: [uploadPlugin],});

Users can paste images directly into the editor, drag and drop files, or use the /image slash command. All three paths call your onUpload handler and insert the returned URL into the email content.

Building a Custom Extension

If you need a block type the editor does not ship with a custom button, a social link row, a branded header, or anything else, you create it as a TipTap extension with an email serializer attached.

import { Node } from "@tiptap/core";import { Section, Button } from "react-email";const CallToActionBlock = Node.create({  name: "callToAction",  group: "block",  atom: true,  addAttributes() {    return {      text: { default: "Click here" },      href: { default: "#" },      backgroundColor: { default: "#149a54" },    };  },  // How it renders in the editor (browser HTML)  renderHTML({ HTMLAttributes }) {    return ["div", { class: "cta-block", ...HTMLAttributes }];  },  // How it serializes to React Email components  toReactEmail({ node }) {    return (      <Section>        <Button          href={node.attrs.href}          style={{ backgroundColor: node.attrs.backgroundColor }}        >          {node.attrs.text}        </Button>      </Section>    );  },});

The toReactEmail method is where the email-awareness lives. Every custom node you create needs this method to tell the serializer how to convert the node into React Email components.

Styling the Editor

The editor's own UI (the editable area, bubble menus, slash command palette) is unstyled by default. You control the appearance through CSS.

The editor applies class names you can target:

/* Editor content area */.ProseMirror {  outline: none;  padding: 16px;  min-height: 400px;}/* Placeholder text */.ProseMirror p.is-editor-empty:first-child::before {  color: #adb5bd;  content: attr(data-placeholder);  float: left;  height: 0;  pointer-events: none;}/* Selected text highlight */.ProseMirror ::selection {  background-color: #c8f5dc;}

For the email output styling, use the theming plugin. For the editor shell (container, toolbar, borders), write regular CSS or Tailwind classes on your wrapper elements.

Storing and Loading Editor State

The editor's document state serializes to JSON. Store this in your database alongside the rendered HTML.

// Save editor stateconst editorState = editor.getJSON();const emailHtml = await exportEmail(editor);await db.emailTemplate.upsert({  where: { id: templateId },  update: {    editorJson: JSON.stringify(editorState),    renderedHtml: emailHtml,    updatedAt: new Date(),  },  create: {    id: templateId,    editorJson: JSON.stringify(editorState),    renderedHtml: emailHtml,  },});// Load editor stateconst template = await db.emailTemplate.findUnique({ where: { id: templateId } });const editor = useEditor({  extensions: [StarterKit],  content: JSON.parse(template.editorJson), // Restore from JSON});

Keep both the JSON (for editing) and the rendered HTML (for sending). Re-rendering from JSON every time you send adds latency. Cache the HTML and only re-render when the template changes.

What to Watch Out For

Email client compatibility is still your responsibility for custom extensions. The built-in extensions are tested against Gmail, Outlook, Apple Mail, and major mobile clients. If you build a custom extension, test the output HTML yourself. What looks fine in a browser can break silently in Outlook.

The editor is a client component. It uses browser APIs for the editing experience. In a Next.js App Router project, your editor component must be a Client Component ("use client"). The export and render step can be server-side.

Large tables can get heavy. The editor handles tables well, but very large data tables in email are already a bad pattern anyway both for rendering compatibility and for readability. If your users try to build a spreadsheet in an email template, that is a UX problem, not an editor problem.

Quick Integration Checklist

  • Install @react-email/editor alongside react-email
  • Create a client component that wraps useEditor and EditorContent
  • Add StarterKit as the base extension
  • Wire up BubbleMenu for inline formatting
  • Add SlashCommands for block insertion
  • Integrate ImageUploadPlugin with your file upload endpoint
  • Implement composeReactEmail + render() for HTML export
  • Store editor JSON in your database for template persistence
  • Test exported HTML in at least Gmail, Outlook, and a mobile client

Final Thoughts

The React Email Editor fills a gap that most email tooling ignores: the space between "write code" and "use a third-party SaaS builder."

If you build products that involve email and most products do having a native, embeddable editor means you stop sending users to Mailchimp just to edit a subject line. You control the interface, the output, and the data. Users get a clean editing experience that lives inside your product instead of requiring a tab switch.

Built on TipTap and ProseMirror, it is extensible enough to cover custom use cases without requiring you to build an email editor from scratch. The 35+ email-aware extensions handle the hard parts of email-safe serialization, multi-column table layouts, inline styles, and more. Also you focus on integrating it into your product.

For any team shipping email features in a React or Next.js product, this is now the path worth exploring first.

Source: React Email Editor Documentation · Full Email Builder Example

Want help embedding the React Email Editor into your SaaS product? Websyro Agency builds custom email workflows and editor integrations for product teams. Talk to us first consultation is free.

Related Blogs

View all