Sending & Testing

Embed the React Email editor

Add the open-source React Email visual editor to your own app: install it, mount the base editor, theme it, add the Inspector sidebar, and send the exported HTML with POST /emails.

The React Email editor is an open-source visual editor that lets your users author email templates inside your own product. It ships with bubble menus, slash commands, an Inspector sidebar, and extensible nodes, and it exports email-ready HTML you can hand straight to MailBlastr. This guide covers embedding it in a React app, theming it, customizing the Inspector, and sending the result.

Prerequisites

  • A React 18+ application (Vite, Next.js, or Remix, for example).
  • A bundler with package exports support (the default for the frameworks above).
  1. 1
    Install the editor

    Add @react-email/editor and import the bundled theme stylesheet once at your app root.

  2. 2
    Mount the base editor

    The standalone EmailEditor component comes with the default extensions, menus, and theming wired up.

  3. 3
    Style the editor

    Override the --re-* CSS variables on a wrapper class to match your brand; the default theme also follows prefers-color-scheme and a .dark ancestor class.

  4. 4
    Add the Inspector sidebar

    Pass Inspector.Root as a child of EmailEditor so it shares the editor context and swaps panels based on the selection.

  5. 5
    Send the result with MailBlastr

    Read the exported HTML via the editor ref, POST it to your own server, and forward it to MailBlastr.

Install

npm install @react-email/editor
app/layout.tsx
import '@react-email/editor/themes/default.css';

Mount the base editor

app/components/Editor.tsx
'use client';

import { EmailEditor } from '@react-email/editor';

const initialContent = `
  <h1>Welcome</h1>
  <p>Type "/" for a command, or select text to format it.</p>
`;

export function Editor() {
  return <EmailEditor content={initialContent} />;
}

content accepts an HTML string or a TipTap JSON document. For full control over which extensions load, drop down to the lower-level setup with EditorProvider.

Theme it to your brand

The editor exposes two styling layers: the chrome (menus, palette, Inspector) driven by --re-* custom properties and data-re-* selectors, and the content area styled via TipTap’s .tiptap class. Override the variables on a wrapper class:

app/styles/editor.css
.brand-editor {
  --re-bg: #fafaf9;
  --re-border: #d6d3d1;
  --re-text: #1c1917;
  --re-text-muted: #78716c;
  --re-hover: rgba(28, 25, 23, 0.04);
  --re-active: rgba(28, 25, 23, 0.08);
  --re-radius: 0.5rem;
}

[data-re-bubble-menu-item][data-active] {
  background: #2563eb;
  color: #fff;
}

.tiptap h1 {
  font-size: 1.75rem;
  letter-spacing: -0.025em;
}

Add the Inspector sidebar

The Inspector is a contextual sidebar that swaps panels based on the current selection — document defaults, node properties, or text formatting. Pass Inspector.Root as a child of EmailEditor so it shares the editor context:

app/components/Editor.tsx
'use client';

import { EmailEditor } from '@react-email/editor';
import { Inspector } from '@react-email/editor/ui';

export function Editor() {
  return (
    <div className="flex" style={{ height: '600px' }}>
      <EmailEditor
        content="<h1>Hello</h1><p>Click any element to inspect it.</p>"
        className="flex-1 min-w-0 overflow-y-auto p-4"
      >
        <Inspector.Root className="w-72 shrink-0 border-l p-4 overflow-y-auto">
          <Inspector.Breadcrumb />
          <Inspector.Document />
          <Inspector.Node />
          <Inspector.Text />
        </Inspector.Root>
      </EmailEditor>
    </div>
  );
}

Send the exported HTML with MailBlastr

The standalone editor exposes its output through ref methods. Read the HTML in a handler, POST it to your server, and forward it to MailBlastr with POST /emails — never call MailBlastr from the browser, where your key would be exposed.

MethodReturnsDescription
getEmailHTML()Promise<string>Email-ready HTML
getEmailText()Promise<string>Plain-text version
getEmail()Promise<{ html, text }>Both in one call
import { EmailEditor, type EmailEditorRef } from '@react-email/editor';
import { useRef } from 'react';

export function MyEditor() {
  const ref = useRef<EmailEditorRef>(null);

  const handleExport = async () => {
    if (!ref.current) return;
    const html = await ref.current.getEmailHTML();
    // Send via YOUR server-side endpoint — the key stays on the server.
    await fetch('/api/send-email', {
      method: 'POST',
      body: JSON.stringify({ html }),
    });
  };

  return (
    <div>
      <EmailEditor ref={ref} content="<p>Edit me</p>" />
      <button onClick={handleExport}>Export &amp; send</button>
    </div>
  );
}
app/api/send-email/route.ts (your server)
import { MailBlastr } from 'mailblastr';

const mb = new MailBlastr(process.env.MAILBLASTR_API_KEY);

export async function POST(req: Request) {
  const { html } = await req.json();

  const { data, error } = await mb.emails.send({
    from: 'Acme <hello@yourdomain.com>',
    to: ['customer@example.com'],
    subject: 'Your newsletter',
    html,
  });

  if (error) return Response.json(error, { status: 422 });
  return Response.json(data);
}
Want to store reusable templates centrally instead of exporting raw HTML each time? See Template emails with React Email and the Templates overview.