> ## Documentation Index
> Fetch the complete documentation index at: https://react.email/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Email Export

> Convert editor content to email-ready HTML.

## Quick start

If you use the standalone `EmailEditor` component, you can access the email HTML and plain text through ref methods.

```tsx theme={"theme":{"light":"github-light","dark":"vesper"}}
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 theme={"theme":{"light":"github-light","dark":"vesper"}}
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 theme={"theme":{"light":"github-light","dark":"vesper"}}
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 theme={"theme":{"light":"github-light","dark":"vesper"}}
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 theme={"theme":{"light":"github-light","dark":"vesper"}}
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 theme={"theme":{"light":"github-light","dark":"vesper"}}
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>
