Overview
The Email Builder example demonstrates how to create an email composition tool with templates, signatures, split-view editing, HTML preview, and email-safe export.
Live Demo Try the interactive email builder demo
Features
Email Templates Pre-built templates for common email types
Split View Edit and preview side-by-side
HTML Preview See the generated email HTML in real-time
Email-Safe Export Export with inline styles for email clients
Implementation
Email Templates
Pre-built templates for different email types:
const EMAIL_TEMPLATES = [
{
id: 'blank' ,
name: 'Blank Email' ,
subject: '' ,
content: { /* Empty Yoopta value */ },
},
{
id: 'welcome' ,
name: 'Welcome Email' ,
subject: 'Welcome to Our Platform!' ,
content: {
'block-1' : {
type: 'Paragraph' ,
value: [{
type: 'paragraph' ,
children: [{ text: 'Hi there,' }],
}],
},
'block-2' : {
type: 'Paragraph' ,
value: [{
type: 'paragraph' ,
children: [
{ text: 'Welcome to ' },
{ text: 'Our Platform' , bold: true },
{ text: '! We \' re excited to have you on board.' },
],
}],
},
// More blocks...
},
},
{
id: 'newsletter' ,
name: 'Newsletter' ,
subject: 'Monthly Newsletter - [Month]' ,
content: { /* Newsletter template */ },
},
];
Email Builder Component
import { useState , useMemo } from 'react' ;
import YooptaEditor , { createYooptaEditor } from '@yoopta/editor' ;
import { EMAIL_PLUGINS , EMAIL_MARKS } from './config' ;
import { Button } from '@/components/ui/button' ;
import { Input } from '@/components/ui/input' ;
import { Send , Eye , Code } from 'lucide-react' ;
function EmailBuilder () {
const [ subject , setSubject ] = useState ( '' );
const [ to , setTo ] = useState ( '' );
const [ viewMode , setViewMode ] = useState < 'edit' | 'preview' | 'html' >( 'edit' );
const editor = useMemo (() => {
return createYooptaEditor ({
plugins: EMAIL_PLUGINS ,
marks: EMAIL_MARKS ,
});
}, []);
return (
< div className = "h-screen flex flex-col" >
{ /* Email Header */ }
< div className = "border-b p-4 space-y-3" >
< div className = "flex gap-2" >
< Input
placeholder = "To:"
value = { to }
onChange = { ( e ) => setTo ( e . target . value ) }
/>
< Button onClick = { handleSend } >
< Send className = "h-4 w-4 mr-2" />
Send
</ Button >
</ div >
< Input
placeholder = "Subject:"
value = { subject }
onChange = { ( e ) => setSubject ( e . target . value ) }
/>
</ div >
{ /* Toolbar */ }
< div className = "border-b p-2 flex gap-2" >
< Button
size = "sm"
variant = { viewMode === 'edit' ? 'default' : 'outline' }
onClick = { () => setViewMode ( 'edit' ) }
>
Edit
</ Button >
< Button
size = "sm"
variant = { viewMode === 'preview' ? 'default' : 'outline' }
onClick = { () => setViewMode ( 'preview' ) }
>
< Eye className = "h-4 w-4 mr-2" />
Preview
</ Button >
< Button
size = "sm"
variant = { viewMode === 'html' ? 'default' : 'outline' }
onClick = { () => setViewMode ( 'html' ) }
>
< Code className = "h-4 w-4 mr-2" />
HTML
</ Button >
</ div >
{ /* Editor Area */ }
< div className = "flex-1 overflow-auto" >
{ viewMode === 'edit' && (
< div className = "max-w-3xl mx-auto p-8" >
< YooptaEditor
editor = { editor }
placeholder = "Compose your email..."
/>
</ div >
) }
{ viewMode === 'preview' && (
< EmailPreview content = { editor . getEmail () } />
) }
{ viewMode === 'html' && (
< HTMLPreview html = { editor . getEmail () } />
) }
</ div >
</ div >
);
}
Email-Safe Plugins
Limit to email-compatible features:
import Paragraph from '@yoopta/paragraph' ;
import Headings from '@yoopta/headings' ;
import Lists from '@yoopta/lists' ;
import Blockquote from '@yoopta/blockquote' ;
import Image from '@yoopta/image' ;
import Link from '@yoopta/link' ;
import Divider from '@yoopta/divider' ;
import { Bold , Italic , Underline , Strike } from '@yoopta/marks' ;
export const EMAIL_PLUGINS = [
Paragraph ,
Headings . HeadingOne ,
Headings . HeadingTwo ,
Headings . HeadingThree ,
Lists . BulletedList ,
Lists . NumberedList ,
Blockquote ,
Image . extend ({
options: {
// Ensure images are hosted and accessible
async onUpload ( file ) {
const url = await uploadToEmailCDN ( file );
return { src: url };
},
},
}),
Link ,
Divider ,
];
export const EMAIL_MARKS = [ Bold , Italic , Underline , Strike ];
Template Selector
import {
DropdownMenu ,
DropdownMenuContent ,
DropdownMenuItem ,
DropdownMenuTrigger ,
} from '@/components/ui/dropdown-menu' ;
import { FileText } from 'lucide-react' ;
function TemplateSelector ({ onSelect } : { onSelect : ( template : Template ) => void }) {
return (
< DropdownMenu >
< DropdownMenuTrigger asChild >
< Button size = "sm" variant = "outline" >
< FileText className = "h-4 w-4 mr-2" />
Templates
</ Button >
</ DropdownMenuTrigger >
< DropdownMenuContent >
{ EMAIL_TEMPLATES . map (( template ) => (
< DropdownMenuItem
key = { template . id }
onClick = { () => onSelect ( template ) }
>
{ template . name }
</ DropdownMenuItem >
)) }
</ DropdownMenuContent >
</ DropdownMenu >
);
}
Email Export
Export with inline styles for email clients:
import { useYooptaEditor } from '@yoopta/editor' ;
function useEmailExport () {
const editor = useYooptaEditor ();
const exportEmail = () => {
// Get email-safe HTML with inline styles
const html = editor . getEmail ();
// Wrap in email template
const emailHTML = `
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title> ${ subject } </title>
</head>
<body style="margin: 0; padding: 0; font-family: Arial, sans-serif;">
<table width="100%" cellpadding="0" cellspacing="0" style="max-width: 600px; margin: 0 auto;">
<tr>
<td style="padding: 20px;">
${ html }
</td>
</tr>
</table>
</body>
</html>
` ;
return emailHTML ;
};
return { exportEmail };
}
HTML Preview Component
import { ScrollArea } from '@/components/ui/scroll-area' ;
import { Button } from '@/components/ui/button' ;
import { Copy , Check } from 'lucide-react' ;
import { useState } from 'react' ;
function HTMLPreview ({ html } : { html : string }) {
const [ copied , setCopied ] = useState ( false );
const handleCopy = async () => {
await navigator . clipboard . writeText ( html );
setCopied ( true );
setTimeout (() => setCopied ( false ), 2000 );
};
return (
< div className = "p-4" >
< div className = "flex justify-between items-center mb-4" >
< h3 className = "font-semibold" > Email HTML </ h3 >
< Button size = "sm" onClick = { handleCopy } >
{ copied ? (
<>< Check className = "h-4 w-4 mr-2" /> Copied! </>
) : (
<>< Copy className = "h-4 w-4 mr-2" /> Copy HTML </>
) }
</ Button >
</ div >
< ScrollArea className = "h-[600px] rounded border" >
< pre className = "p-4 text-xs" >
< code > { html } </ code >
</ pre >
</ ScrollArea >
</ div >
);
}
Email Preview Component
function EmailPreview ({ content } : { content : string }) {
return (
< div className = "max-w-3xl mx-auto p-8" >
< div className = "border rounded-lg overflow-hidden" >
{ /* Email header */ }
< div className = "bg-neutral-100 p-4 border-b" >
< div className = "text-sm space-y-1" >
< div >< strong > From: </ strong > [email protected] </ div >
< div >< strong > To: </ strong > { to } </ div >
< div >< strong > Subject: </ strong > { subject } </ div >
</ div >
</ div >
{ /* Email body */ }
< div
className = "p-8 bg-white"
dangerouslySetInnerHTML = { { __html: content } }
/>
</ div >
</ div >
);
}
Send Email Function
function useSendEmail () {
const editor = useYooptaEditor ();
const sendEmail = async ({
to ,
subject ,
} : {
to : string ;
subject : string ;
}) => {
const html = editor . getEmail ();
const response = await fetch ( '/api/send-email' , {
method: 'POST' ,
headers: { 'Content-Type' : 'application/json' },
body: JSON . stringify ({
to ,
subject ,
html ,
}),
});
if ( ! response . ok ) {
throw new Error ( 'Failed to send email' );
}
return response . json ();
};
return { sendEmail };
}
Email Compatibility
Email clients have limited CSS support. The getEmail() method exports HTML with inline styles for maximum compatibility.
Supported Features
Basic text formatting (bold, italic, underline)
Headings and paragraphs
Lists (bulleted and numbered)
Images (must be hosted)
Links
Dividers
Not Recommended
Custom fonts (use web-safe fonts)
Complex layouts (use tables)
Advanced CSS (flexbox, grid)
JavaScript
Video embeds
Source Code
View Full Source Complete email builder implementation on GitHub
Use Cases
Email Campaigns Create marketing emails with templates
Newsletters Design and send company newsletters
Transactional Emails Build order confirmations and notifications
Email Signatures Create rich email signatures
Next Steps
README Editor Learn about the Markdown editor
Exports Explore all export options