Files
2026-04-29 09:29:56 -03:00

239 lines
7.0 KiB
Plaintext

---
title: "Email Export"
sidebarTitle: "Email Export"
description: "Convert editor content to email-ready HTML."
icon: "file-export"
---
## Quick start
If you use the standalone `EmailEditor` component, you can access the email HTML and plain text through ref methods.
```tsx
import { EmailEditor, type EmailEditorRef } from '@react-email/editor';
import { useRef, useState } from 'react';
export function MyEditor() {
const ref = useRef<EmailEditorRef>(null);
const [html, setHtml] = useState('');
const handleExport = async () => {
if (!ref.current) return;
const html = await ref.current.getEmailHTML();
setHtml(html);
};
return (
<div>
<EmailEditor ref={ref} content="<p>Edit me</p>" />
<button onClick={handleExport}>Export HTML</button>
{html && <textarea readOnly value={html} />}
</div>
);
}
```
The ref exposes three export methods:
| Method | Returns | Description |
|--------|---------|-------------|
| `getEmailHTML()` | `Promise<string>` | Email-ready HTML |
| `getEmailText()` | `Promise<string>` | Plain text version |
| `getEmail()` | `Promise<{ html, text }>` | Both in a single call |
All three use [composeReactEmail](/editor/api-reference/compose-react-email) under the hood.
## Using without EmailEditor
Use [composeReactEmail](/editor/api-reference/compose-react-email) directly to convert editor content into email-ready HTML and plain text without [EmailEditor](/editor/api-reference/email-editor).
```tsx
import { composeReactEmail } from '@react-email/editor/core';
import { StarterKit } from '@react-email/editor/extensions';
import { EmailTheming } from '@react-email/editor/plugins';
import { BubbleMenu } from '@react-email/editor/ui';
import { EditorProvider, useCurrentEditor } from '@tiptap/react';
import { useState } from 'react';
const extensions = [StarterKit, EmailTheming];
const content = `
<h1>My Email Newsletter</h1>
<p>Edit this content, then click <strong>Export HTML</strong> to see the generated email markup.</p>
<p>The exported HTML uses React Email components and is ready to send.</p>
`;
function ExportPanel() {
const { editor } = useCurrentEditor();
const [html, setHtml] = useState('');
const [exporting, setExporting] = useState(false);
const handleExport = async () => {
if (!editor) return;
setExporting(true);
const result = await composeReactEmail({ editor, preview: null });
setHtml(result.html);
setExporting(false);
};
return (
<div>
<button onClick={handleExport} disabled={exporting}>
{exporting ? 'Exporting...' : 'Export HTML'}
</button>
{html && (
<textarea readOnly value={html} rows={16} style={{ width: '100%', fontFamily: 'monospace' }} />
)}
</div>
);
}
export function MyEditor() {
return (
<EditorProvider extensions={extensions} content={content}>
<BubbleMenu />
<ExportPanel />
</EditorProvider>
);
}
```
## How it works
The `composeReactEmail` function follows this pipeline:
1. **Read** the editor's JSON document
2. **Traverse** each node and mark in the document tree
3. **Call** `renderToReactEmail()` on each `EmailNode` and `EmailMark` extension
4. **Apply** theme styles via the `SerializerPlugin` (if `EmailTheming` is configured)
5. **Wrap** the content in a `BaseTemplate` component
6. **Renders** to an HTML string and plain text version using [render](/utilities/render)
The return value is:
```tsx
const { html, text } = await composeReactEmail({ editor, preview: null });
// html — Full HTML email string, ready to send
// text — Plain text version for email clients that don't support HTML
```
## Preview text
The `preview` parameter sets the email preview text.
Email preview text is the snippet shown in inbox list views before the email is opened.
```tsx
const result = await composeReactEmail({
editor,
preview: 'Check out our latest updates!',
});
```
Pass `null` to omit preview text.
## Using with theming
When the `EmailTheming` extension is in your extensions array, theme styles are automatically
injected into the exported HTML.
The serializer uses the `SerializerPlugin` provided by `EmailTheming` to resolve styles for each node based on the current theme and depth in the
document tree.
```tsx
const extensions = [StarterKit, EmailTheming.configure({ theme: 'basic' })];
// Later, when exporting:
const { html } = await composeReactEmail({ editor, preview: null });
// ...the html includes all theme styles inline
```
## Full example with export panel
Here's a complete editor with theming and an export panel.
```tsx
import { composeReactEmail } from '@react-email/editor/core';
import { StarterKit } from '@react-email/editor/extensions';
import { EmailTheming } from '@react-email/editor/plugins';
import {
BubbleMenu,
defaultSlashCommands,
SlashCommand,
} from '@react-email/editor/ui';
import { EditorProvider, useCurrentEditor } from '@tiptap/react';
import { useState } from 'react';
type EditorTheme = 'basic' | 'minimal';
function ControlPanel() {
const { editor } = useCurrentEditor();
const [html, setHtml] = useState('');
const [exporting, setExporting] = useState(false);
const handleExport = async () => {
if (!editor) return;
setExporting(true);
const result = await composeReactEmail({ editor, preview: null });
setHtml(result.html);
setExporting(false);
};
return (
<div>
<button onClick={handleExport} disabled={exporting}>
{exporting ? 'Exporting...' : 'Export HTML'}
</button>
{html && (
<textarea
readOnly
value={html}
rows={16}
style={{ width: '100%', fontFamily: 'monospace', fontSize: '12px' }}
/>
)}
</div>
);
}
export function FullEmailBuilder() {
const [theme, setTheme] = useState<EditorTheme>('basic');
const extensions = [StarterKit, EmailTheming.configure({ theme })];
const content = `
<h1>Weekly Newsletter</h1>
<p>Edit this content, then click <strong>Export HTML</strong> to see the generated email markup.</p>
`;
return (
<div>
<div style={{ display: 'flex', gap: '8px', marginBottom: '16px' }}>
<button onClick={() => setTheme('basic')}>Basic Theme</button>
<button onClick={() => setTheme('minimal')}>Minimal Theme</button>
</div>
<EditorProvider key={theme} extensions={extensions} content={content}>
<BubbleMenu hideWhenActiveNodes={['button']} hideWhenActiveMarks={['link']} />
<BubbleMenu.LinkDefault />
<BubbleMenu.ButtonDefault />
<SlashCommand items={defaultSlashCommands} />
<ControlPanel />
</EditorProvider>
</div>
);
}
```
## Examples
See email export in action with runnable examples:
<CardGroup cols={2}>
<Card title="Email Export" icon="code" href="https://react.email/editor/examples/email-export">
Export editor content to themed HTML.
</Card>
<Card title="Full Email Builder" icon="code" href="https://react.email/editor/examples/full-email-builder">
Complete editor with theming, menus, and export.
</Card>
</CardGroup>