mirror of
https://github.com/resend/react-email.git
synced 2026-09-14 14:18:02 +08:00
239 lines
7.0 KiB
Plaintext
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>
|