@usemonocle/visual-email-editor
v0.0.1
Published
A headless, customizable email template builder built with React, TypeScript, and modern web technologies
Maintainers
Readme
Visual Email Editor - Tabular Clone
A headless, customizable email template builder built with React, TypeScript, and modern web technologies. Inspired by Tabular.email, this editor allows you to import HTML strings, visually edit them, and export email-safe HTML.
🎯 Goals
- HTML Import: Import HTML strings from API or user paste
- Visual Editing: Drag-and-drop interface with full control
- HTML Export: Export email-safe HTML that works across 50+ email clients
- Headless Architecture: Build your own components, full control
- Responsive Preview: Desktop and mobile preview modes
- Component Library: Reusable email blocks
🏗️ Architecture
Core Libraries
State Management:
- Zustand - Lightweight, unopinionated state management
- Simple store for editor state (nodes, selection, history)
Drag & Drop:
- @dnd-kit/core - Modern, accessible drag-and-drop
- @dnd-kit/sortable - Sortable list functionality
- @dnd-kit/utilities - Helper utilities
Rich Text Editing:
- @tiptap/react - Headless rich text editor
- @tiptap/starter-kit - Essential extensions
- @tiptap/extension-text-align - Text alignment
- @tiptap/extension-color - Text color
- @tiptap/extension-text-style - Text styling
HTML Parsing:
- node-html-parser - Fast, browser-compatible HTML parser
- Parse HTML strings into component tree
- Extract styles, attributes, and structure
HTML Export:
- ReactDOMServer - Render React components to HTML
- juice or inline-css - Inline CSS for email compatibility
UI Components:
- shadcn/ui - Re-usable components built on Radix UI and Tailwind CSS
- Copy components directly into your project
- Fully customizable
- Built on Radix UI primitives
- Components: Popover, Select, Slider, Separator, Label, Toggle Group, Button, Input, Card, etc.
- Tailwind CSS - Utility-first CSS framework
- Lucide React - Icon library
⚠️ Development Guidelines
Use shadcn/ui Components
Do NOT create custom UI components. Always use shadcn/ui components when possible.
- ✅ Use
Popoverfor floating toolbars and menus - ✅ Use
Dialogfor modals - ✅ Use
ToggleGroupfor button groups - ✅ Use
Selectfor dropdowns - ✅ Use
Button,Input,Labelfor form controls - ✅ Keep each React component in a dedicated file (avoid multiple inline component definitions in a single file)
- ❌ Do NOT create custom floating divs with manual positioning
- ❌ Do NOT create custom modal implementations
- ❌ Do NOT reinvent existing Radix UI primitives
shadcn/ui components provide:
- Built-in accessibility (ARIA attributes, focus management)
- Collision detection (won't go off-screen)
- Consistent styling with the design system
- Less custom code to maintain
To add a new shadcn component:
npx shadcn@latest add [component-name]Utilities:
- clsx - Conditional className utility
- tailwind-merge - Merge Tailwind classes
- class-variance-authority - Component variants
📦 Required Dependencies
Core Dependencies
{
"dependencies": {
"react": "^19.2.0",
"react-dom": "^19.2.0",
"zustand": "^4.5.0",
"@dnd-kit/core": "^6.1.0",
"@dnd-kit/sortable": "^8.0.0",
"@dnd-kit/utilities": "^3.2.2",
"@tiptap/react": "^3.13.0",
"@tiptap/starter-kit": "^3.13.0",
"@tiptap/extension-text-align": "^3.13.0",
"@tiptap/extension-color": "^3.13.0",
"@tiptap/extension-text-style": "^3.13.0",
"node-html-parser": "^6.1.0",
"juice": "^10.0.0",
"@radix-ui/react-popover": "^1.1.15",
"@radix-ui/react-select": "^2.2.6",
"@radix-ui/react-slider": "^1.3.6",
"@radix-ui/react-separator": "^1.1.8",
"@radix-ui/react-label": "^2.1.8",
"@radix-ui/react-toggle-group": "^1.1.11",
"@radix-ui/react-slot": "^1.2.4",
"@radix-ui/react-dialog": "^1.1.15",
"@radix-ui/react-tabs": "^1.1.1",
"@radix-ui/react-dropdown-menu": "^2.1.15",
"tailwindcss": "^4.1.17",
"lucide-react": "^0.556.0",
"clsx": "^2.1.1",
"tailwind-merge": "^3.4.0",
"class-variance-authority": "^0.7.1"
}
}🏛️ Architecture Overview
┌─────────────────────────────────────────┐
│ Email Editor Store │
│ (Zustand) │
│ - Nodes tree │
│ - Selection state │
│ - History (undo/redo) │
│ - Viewport mode (desktop/mobile) │
└─────────────────────────────────────────┘
│
┌───────────┴───────────┐
│ │
┌───────▼────────┐ ┌─────────▼────────┐
│ Canvas Area │ │ Settings Panel │
│ (DnD Kit) │ │ (shadcn/ui) │
│ │ │ │
│ - Drag zones │ │ - Property edits │
│ - Drop zones │ │ - Style controls│
│ - Selection │ │ - Layout options│
└────────────────┘ └──────────────────┘
│
┌───────▼─────────────────────────────────┐
│ Component Library │
│ - Container (horizontal/vertical) │
│ - Text (TipTap) │
│ - Image │
│ - Button │
│ - Divider │
│ - Spacer │
└──────────────────────────────────────────┘🚀 Implementation Plan
Phase 1: Core Infrastructure
State Management (Zustand)
- Editor store with nodes tree
- Selection state
- History stack (undo/redo)
- Viewport mode state
Drag & Drop Setup (@dnd-kit)
- DndContext provider
- Sortable containers
- Drag handles
- Drop zones
Component System
- Base component interface
- Component registry
- Props system
Reliability and Performance
- Autosave (local/session) and recover flow
- Undo/redo stack limits and batching
- Large-template rendering strategy (virtualization or chunked renders)
- Throttled CSS inlining/export for big documents
Phase 2: Core Components
Container Components
- Horizontal frame
- Vertical frame
- Canvas container
- Table-based rows and columns with per-cell padding/margins
- Column stacking rules for mobile
Content Components
- Text block (TipTap integration)
- Image block
- Button block
- Divider block
- Spacer block
- VML-safe button variant for Outlook
Component Features
- Selection highlighting
- Drag handles
- Resize handles (optional)
- Context menu
- Hover duplicate/delete controls
- Quick alignment and spacing controls
Phase 3: Editor UI
Toolbar
- Component palette
- Undo/redo
- Preview toggle
- Export button
- Copy HTML
- Copy shareable link
- Send test email
Settings Panel (shadcn/ui components)
- Property editor (Input, Select)
- Style controls (Slider, Color Picker)
- Layout options (Toggle Group)
- Visibility toggle (Switch)
- Dynamic content support
Viewport
- Desktop preview
- Mobile preview
- Responsive toggle
- Grid guides and table outlines
Phase 4: HTML Import/Export
HTML Parser (node-html-parser)
- Parse HTML string
- Extract component structure
- Map HTML elements to components
- Extract styles and attributes
Component Mapper
- HTML → Component registry
- Style extraction
- Attribute mapping
- Nested structure handling
HTML Exporter
- Component tree → HTML
- ReactDOMServer rendering
- CSS inlining (juice)
- Email-safe HTML generation
- Email safety validation (disallow unsafe CSS/attrs, require alt text)
- Table structure validation
- Copy HTML action
- Send test email hook
Phase 5: Advanced Features
Templates
- Save templates
- Load templates
- Template library
- Starter gallery and random template loader
Responsive Design
- Mobile-specific styles
- Breakpoint handling
- Preview modes
Email Client Compatibility
- Outlook compatibility
- Gmail compatibility
- Inline CSS
- Table-based layouts
- VML fallbacks for buttons and backgrounds
📁 Project Structure
src/
├── store/
│ ├── editorStore.ts # Zustand store
│ ├── types.ts # TypeScript types
│ └── utils.ts # Store utilities
├── components/
│ ├── editor/
│ │ ├── Canvas.tsx # Main canvas area
│ │ ├── Toolbar.tsx # Top toolbar
│ │ ├── SettingsPanel.tsx # Right sidebar
│ │ ├── ComponentPalette.tsx # Component library
│ │ └── Viewport.tsx # Preview area
│ ├── blocks/
│ │ ├── Container.tsx # Container component
│ │ ├── Text.tsx # Text block (TipTap)
│ │ ├── Image.tsx # Image block
│ │ ├── Button.tsx # Button block
│ │ └── index.ts # Component registry
│ └── ui/ # shadcn/ui components
│ ├── button.tsx
│ ├── input.tsx
│ ├── select.tsx
│ ├── slider.tsx
│ ├── popover.tsx
│ ├── card.tsx
│ └── ...
├── lib/
│ ├── html-parser.ts # HTML → Component tree
│ ├── html-exporter.ts # Component tree → HTML
│ └── email-utils.ts # Email-specific utilities
└── hooks/
├── useDragDrop.ts # DnD Kit hooks
├── useSelection.ts # Selection management
└── useHistory.ts # Undo/redo🔑 Key Features
1. HTML Import
- Parse HTML strings (from API or paste)
- Map to component tree
- Preserve styles and structure
- Handle nested elements
2. Visual Editing
- Drag-and-drop components
- Resize components
- Property editing
- Style customization
- Layout controls
3. HTML Export
- Generate email-safe HTML
- Inline CSS
- Table-based layouts
- Client compatibility
4. Component System
- Custom components
- Reusable blocks
- Component library
- Template system
🎨 Component API
Base Component Interface
interface EmailComponent {
id: string;
type: string;
props: Record<string, any>;
children?: string[];
parentId?: string;
}Component Registry
const componentRegistry = {
container: Container,
text: Text,
image: Image,
button: Button,
// ...
};🔄 Data Flow
HTML String
↓
[HTML Parser]
↓
Component Tree (Zustand Store)
↓
[Editor UI] ← → [User Interactions]
↓
Updated Component Tree
↓
[HTML Exporter]
↓
Email-Safe HTML📝 Implementation Checklist
Core Setup
- [ ] Install all dependencies
- [ ] Set up Zustand store
- [ ] Configure DnD Kit
- [ ] Set up TipTap
- [ ] Configure HTML parser
Components
- [ ] Container (horizontal/vertical)
- [ ] Text block (TipTap)
- [ ] Image block
- [ ] Button block
- [ ] Divider block
- [ ] Spacer block
Editor Features
- [ ] Drag and drop
- [ ] Selection system
- [ ] Settings panel
- [ ] Toolbar
- [ ] Component palette
Import/Export
- [ ] HTML parser
- [ ] Component mapper
- [ ] HTML exporter
- [ ] CSS inliner
Advanced
- [ ] Undo/redo
- [ ] Templates
- [ ] Responsive preview
- [ ] Email client testing
🚦 Getting Started
Install Dependencies
bun installSet up shadcn/ui
npx shadcn@latest initThen add components as needed:
npx shadcn@latest add button npx shadcn@latest add input npx shadcn@latest add select npx shadcn@latest add slider npx shadcn@latest add popover npx shadcn@latest add card npx shadcn@latest add label npx shadcn@latest add separator npx shadcn@latest add toggle-group npx shadcn@latest add dialog npx shadcn@latest add tabsStart Development Server
bun run devBuild for Production
bun run build
📚 Resources
- @dnd-kit Documentation
- Zustand Documentation
- TipTap Documentation
- node-html-parser
- shadcn/ui Documentation
- Email HTML Best Practices
🎯 Comparison with Tabular
| Feature | Tabular | This Editor | | ------------------ | ------- | ------------- | | HTML Import | ✅ | ✅ (Custom) | | HTML Export | ✅ | ✅ (Custom) | | Drag & Drop | ✅ | ✅ (@dnd-kit) | | Component Library | ✅ | ✅ (Custom) | | Responsive Preview | ✅ | ✅ (Planned) | | Templates | ✅ | ✅ (Planned) | | Headless | ❌ | ✅ | | Full Control | ❌ | ✅ |
🤝 Contributing
This is a custom implementation. All components are built from scratch for maximum control and flexibility.
📄 License
[Your License Here]
