-
Notifications
You must be signed in to change notification settings - Fork 1k
chore(docs): add editor guides and API references #3104
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 1 commit
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
81de602
Revamp editor docs and split API references
joaopcm 649d20f
fix(docs): format docs.json to pass biome lint
joaopcm 6489c51
Remove Editor section from docs navigation
joaopcm 4c2197a
Improve editor bubble menu docs
joaopcm ebe937d
Expand editor UI docs with composition examples
joaopcm 35d1a24
Expand composeReactEmail docs
joaopcm cc720ad
Remove extra editor install dependency
joaopcm e4aa7dc
Update apps/docs/editor/api-reference/compose-react-email.mdx
joaopcm b716674
Update apps/docs/editor/api-reference/compose-react-email.mdx
joaopcm cc16d0b
Update apps/docs/editor/api-reference/compose-react-email.mdx
joaopcm 897601a
Remove text node Info callout, add .serena to gitignore
joaopcm e06b2ea
Remove SerializerPlugin section from composeReactEmail docs
joaopcm File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,128 @@ | ||
| --- | ||
| title: "composeReactEmail" | ||
| sidebarTitle: "composeReactEmail" | ||
| description: "Convert editor content to email-ready HTML and plain text." | ||
| icon: "file-export" | ||
| --- | ||
|
|
||
| The `composeReactEmail` function serializes the editor's document tree into email-ready HTML | ||
| and a plain text fallback. | ||
|
|
||
| ## Import | ||
|
|
||
| ```tsx | ||
| import { composeReactEmail } from '@react-email/editor/core'; | ||
| ``` | ||
|
|
||
| ## Signature | ||
|
|
||
| ```tsx | ||
| async function composeReactEmail(params: { | ||
| editor: Editor; | ||
| preview: string | null; | ||
| }): Promise<{ html: string; text: string }>; | ||
| ``` | ||
|
|
||
| ## Parameters | ||
|
|
||
| <ResponseField name="editor" type="Editor" required> | ||
| The TipTap editor instance. The function reads the editor's JSON document and walks through | ||
| each registered extension to serialize nodes and marks. | ||
| </ResponseField> | ||
|
|
||
| <ResponseField name="preview" type="string | null" required> | ||
| Preview text shown in inbox list views before the email is opened. Pass `null` to omit. | ||
| </ResponseField> | ||
|
|
||
| ## Return value | ||
|
|
||
| Returns a `Promise` that resolves to an object with: | ||
|
|
||
| | Field | Type | Description | | ||
| |-------|------|-------------| | ||
| | `html` | `string` | Full HTML email string, ready to send | | ||
| | `text` | `string` | Plain text version for email clients that don't support HTML | | ||
|
|
||
| ## How it works | ||
|
|
||
| The serialization pipeline: | ||
|
|
||
| 1. **Read** the editor's JSON document | ||
| 2. **Traverse** each node and mark in the document tree | ||
| 3. **Call** `renderToReactEmail()` on each [`EmailNode`](/editor/api-reference/email-node) and [`EmailMark`](/editor/api-reference/email-mark) extension | ||
| 4. **Apply** theme styles via the `SerializerPlugin` (if [`EmailTheming`](/editor/features/theming) is configured) | ||
| 5. **Wrap** the content in a `BaseTemplate` component | ||
| 6. **Render** to an HTML string and plain text version | ||
|
|
||
| ## Usage | ||
|
|
||
| ### Basic export | ||
|
|
||
| ```tsx | ||
| import { composeReactEmail } from '@react-email/editor/core'; | ||
|
|
||
| const { html, text } = await composeReactEmail({ editor, preview: null }); | ||
| ``` | ||
|
|
||
| ### With preview text | ||
|
|
||
| ```tsx | ||
| const { html, text } = await composeReactEmail({ | ||
| editor, | ||
| preview: 'Check out our latest updates!', | ||
| }); | ||
| ``` | ||
|
|
||
| ### With theming | ||
|
|
||
| When the [`EmailTheming`](/editor/features/theming) extension is in your extensions array, | ||
| theme styles are automatically injected into the exported HTML. No extra configuration needed: | ||
|
|
||
| ```tsx | ||
| import { StarterKit } from '@react-email/editor/extensions'; | ||
| import { EmailTheming } from '@react-email/editor/plugins'; | ||
|
|
||
| const extensions = [StarterKit, EmailTheming.configure({ theme: 'basic' })]; | ||
|
|
||
| // Later, when exporting: | ||
| const { html } = await composeReactEmail({ editor, preview: null }); | ||
| // html includes all theme styles inline | ||
| ``` | ||
|
|
||
| ### Full example with export panel | ||
|
|
||
| ```tsx | ||
| import { composeReactEmail } from '@react-email/editor/core'; | ||
| import { useCurrentEditor } from '@tiptap/react'; | ||
| import { useState } from 'react'; | ||
|
|
||
| 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> | ||
| ); | ||
| } | ||
| ``` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,135 @@ | ||
| --- | ||
| title: "EmailMark" | ||
| sidebarTitle: "EmailMark" | ||
| description: "Base class for creating editor marks with email serialization." | ||
| icon: "paintbrush-fine" | ||
| --- | ||
|
|
||
| `EmailMark` extends TipTap's `Mark` class with an additional `renderToReactEmail()` method | ||
| that controls how the mark is serialized when exporting to email HTML via | ||
| [`composeReactEmail`](/editor/api-reference/compose-react-email). | ||
|
|
||
| Marks are used for inline formatting like bold, italic, links, and custom text annotations. | ||
|
|
||
| ## Import | ||
|
|
||
| ```tsx | ||
| import { EmailMark } from '@react-email/editor/core'; | ||
| ``` | ||
|
|
||
| ## EmailMark.create | ||
|
|
||
| Create a new email-compatible mark from scratch. | ||
|
|
||
| ```tsx | ||
| const Highlight = EmailMark.create({ | ||
| name: 'highlight', | ||
|
|
||
| parseHTML() { | ||
| return [{ tag: 'mark' }]; | ||
| }, | ||
|
|
||
| renderHTML({ HTMLAttributes }) { | ||
| return ['mark', HTMLAttributes, 0]; | ||
| }, | ||
|
|
||
| renderToReactEmail({ children, style }) { | ||
| return ( | ||
| <mark style={{ ...style, backgroundColor: '#fef08a' }}>{children}</mark> | ||
| ); | ||
| }, | ||
| }); | ||
| ``` | ||
|
|
||
| The config object accepts all standard [TipTap Mark options](https://tiptap.dev/docs/editor/api/mark) | ||
| plus the `renderToReactEmail` method. | ||
|
|
||
| ### renderToReactEmail props | ||
|
|
||
| | Prop | Type | Description | | ||
| |------|------|-------------| | ||
| | `children` | `React.ReactNode` | The content wrapped by this mark | | ||
| | `style` | `React.CSSProperties` | Resolved theme styles for this mark (empty object if no theme) | | ||
| | `mark` | `Mark` | The ProseMirror mark instance | | ||
| | `node` | `Node` | The ProseMirror node that contains this mark | | ||
| | `extension` | `EmailMark` | The extension instance, useful for accessing `options` | | ||
|
|
||
| ## EmailMark.from | ||
|
|
||
| Wrap an existing TipTap mark with email serialization support. This is useful when you want to | ||
| reuse a community TipTap extension and add email export support without rewriting it. | ||
|
|
||
| ```tsx | ||
| import { EmailMark } from '@react-email/editor/core'; | ||
| import { Mark } from '@tiptap/core'; | ||
|
|
||
| const MyTipTapMark = Mark.create({ | ||
| name: 'customHighlight', | ||
|
|
||
| parseHTML() { | ||
| return [{ tag: 'mark' }]; | ||
| }, | ||
|
|
||
| renderHTML({ HTMLAttributes }) { | ||
| return ['mark', HTMLAttributes, 0]; | ||
| }, | ||
| }); | ||
|
|
||
| const MyEmailMark = EmailMark.from(MyTipTapMark, ({ children, style }) => { | ||
| return ( | ||
| <mark style={{ ...style, backgroundColor: '#fef08a' }}>{children}</mark> | ||
| ); | ||
| }); | ||
| ``` | ||
|
|
||
| The second argument is the `renderToReactEmail` renderer component. It receives the same props | ||
| as described above. | ||
|
|
||
| ## .configure | ||
|
|
||
| Configure options on an `EmailMark` (same as TipTap's `.configure()`): | ||
|
|
||
| ```tsx | ||
| import { Bold } from '@react-email/editor/extensions'; | ||
|
|
||
| const CustomBold = Bold.configure({ HTMLAttributes: { class: 'custom-bold' } }); | ||
| ``` | ||
|
|
||
| ## .extend | ||
|
|
||
| Extend an `EmailMark` with additional behavior: | ||
|
|
||
| ```tsx | ||
| import { Link } from '@react-email/editor/extensions'; | ||
|
|
||
| const CustomLink = Link.extend({ | ||
| addKeyboardShortcuts() { | ||
| return { | ||
| 'Mod-k': () => this.editor.commands.toggleLink({ href: '' }), | ||
| }; | ||
| }, | ||
| }); | ||
| ``` | ||
|
|
||
| You can also override `renderToReactEmail` when extending: | ||
|
|
||
| ```tsx | ||
| const CustomLink = Link.extend({ | ||
| renderToReactEmail({ children, style, mark }) { | ||
| return ( | ||
| <a | ||
| href={mark.attrs.href} | ||
| style={{ ...style, color: '#0066cc', textDecoration: 'underline' }} | ||
| > | ||
| {children} | ||
| </a> | ||
| ); | ||
| }, | ||
| }); | ||
| ``` | ||
|
|
||
| ## See also | ||
|
|
||
| - [Custom Extensions](/editor/advanced/custom-extensions) — tutorial on building custom extensions | ||
| - [`EmailNode`](/editor/api-reference/email-node) — the equivalent class for block nodes | ||
| - [`composeReactEmail`](/editor/api-reference/compose-react-email) — the function that calls `renderToReactEmail` | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.