As a solo entrepreneur shipping multiple micro SaaS products, I needed email templates constantly. Cold outreach, customer onboarding, password resets, newsletters - the list goes on. But every time I needed one, I faced the same frustrating experience: scattered resources, outdated examples, or paywalled content.
I wanted something simple: a free, community-driven directory of copy-paste email templates. That's why I'm building emailhub.directory.
Inspired by Cursor.directory
I was deeply inspired by the design and approach of cursor.directory, a thriving community platform for Cursor enthusiasts with over 51,000 members. Their clean, functional interface and community-driven model showed me exactly what I wanted to build for email templates.
With approval from the cursor.directory owner, I adapted their proven design patterns and implementation logic to solve the email template discovery problem that I faced daily as a solo entrepreneur. The success of cursor.directory as a hub for sharing development resources demonstrated the power of community-driven platforms, and having permission to learn from their codebase accelerated my development significantly.
Key lessons I borrowed:
- Simplicity first - Clean UI that gets out of the way
- Community-powered - User submissions drive content growth
- Copy-paste ready - Zero friction from discovery to implementation
- Open and free - No paywalls, no artificial limits
- Quality focus - Surface the best content through curation
The Problem with Email Template Discovery
Most email template resources fall into three categories:
- Paid platforms - $50-200/month for templates you'll use once
- Scattered blog posts - Outdated, no standardization, poor formatting
- Design-heavy showcases - Beautiful but not developer-friendly
What developers actually need:
- HTML format (works in any email client)
- Copy-paste ready (no setup required)
- Real use cases (not just "Hello World")
- Free and accessible (no credit card required)
- Community-validated (proven by real usage)
That's exactly what emailhub.directory provides - free, copy-paste ready email templates for every use case, from cold outreach to password resets.
The Tech Stack
I chose a modern, scalable stack optimized for community features and real-time interactions:
Frontend & Framework
- Next.js 15.5 with App Router and React 19
- TypeScript 5.9 in strict mode for type safety
- Tailwind CSS v4 with custom design tokens
- Radix UI for accessible components (dialog, dropdown, tabs)
- Framer Motion for smooth animations
- Lucide React for consistent iconography
Backend & Database
- Supabase for PostgreSQL + authentication + real-time
- Row Level Security (RLS) for data access control
- Full-text search using PostgreSQL
tsvector - Upstash Redis for rate limiting and caching
Rich Text Editing
- Tiptap (ProseMirror) for HTML email editing
- Extensions: tables, lists, syntax highlighting, markdown shortcuts
- Monaco Editor fallback for advanced users
- Real-time preview with placeholder replacement
Email & Content Moderation
- React Email for transactional email templates
- Resend for email delivery (welcome emails, notifications)
- OpenAI Moderation API for content safety
- Server-Sent Events (SSE) for real-time features (planned)
Curious how it works? Browse emailhub.directory to see the platform in action. No signup needed - just click, copy, and use.
Deployment & Infrastructure
- Coolify for self-hosted deployment on my VPS
- GitHub Actions for CI/CD pipeline
- Docker containers for production isolation
- Bun as package manager (3x faster than npm)
Architecture Decisions
1. Server Components by Default
Next.js 15's server components became my default choice. This eliminated client-side data fetching complexity:
// app/templates/page.tsx - Server Component by default
export default async function TemplatesPage() {
// Direct database query - no API route needed
const templates = await getTemplatesWithStats();
return <TemplateGrid templates={templates} />;
}Benefits:
- Zero client-side loading states for initial render
- SEO-friendly (templates indexed by search engines)
- Reduced JavaScript bundle (60% smaller than CSR equivalent)
- Simplified data flow (no Redux, no complex state management)
Client components only when needed:
- Rich text editor
- Form submissions
- Interactive search
- User authentication flows
2. Type-Safe Server Actions with Zod
Next.js server actions are powerful but dangerous - unvalidated user input hits your server directly. On emailhub.directory, I wrapped every action with next-safe-action + Zod schemas:
// lib/actions/submit-template-action.ts
const submitTemplateSchema = z.object({
title: z.string().min(10).max(100),
subject: z.string().min(5).max(200),
content: z.string().min(50).max(10000),
category: z.enum(["sales", "support", "marketing", "transactional"]),
tags: z.array(z.string()).max(5)
});
export const submitTemplate = actionClient
.inputSchema(submitTemplateSchema)
.action(async ({ parsedInput }) => {
// parsedInput is fully typed and validated
const { title, subject, content, category, tags } = parsedInput;
// Run content moderation
const moderationResult = await moderateContent(content);
if (moderationResult.flagged) {
return { error: "Content violates guidelines" };
}
// Save to database with pending status
const template = await createTemplate({
title, subject, content, category, tags,
status: 'pending_review'
});
return { success: true, slug: template.slug };
});Benefits:
- Runtime validation catches bad input before database
- TypeScript inference from Zod schemas (DRY)
- Automatic error handling and type safety
- Impossible to forget validation
- Frontend gets typed actions with full autocomplete
3. OpenAI-Powered Content Moderation
Community platforms have one existential threat: spam and malicious content. I couldn't manually review every submission, so I built an automated moderation pipeline:
// lib/moderation/content-moderator.ts
export async function moderateTemplate(content: string) {
// Step 1: OpenAI Moderation API
const openaiResult = await openai.moderations.create({
input: content,
model: "omni-moderation-latest"
});
const { harassment, hate, violence, sexual } = openaiResult.results[0].category_scores;
// Flag high-risk content for review
if (harassment > 0.8 || hate > 0.9 || violence > 0.8) {
return { decision: "manual_review", reason: "Content policy concern" };
}
// Step 2: HTML Security Checks
const securityFlags = await checkHTMLSecurity(content);
if (securityFlags.hasExternalScripts || securityFlags.hasSuspiciousLinks) {
return { decision: "reject", reason: "Security risk detected" };
}
// Step 3: Quality Checks
const qualityScore = await assessQuality(content);
if (qualityScore < 0.4) {
return { decision: "manual_review", reason: "Low quality - needs review" };
}
// Auto-approve safe, high-quality content
return { decision: "approve", reason: "Passed all checks" };
}Why this approach:
- Fast: 200-300ms latency per submission
- Cost-effective: $0.002 per moderation check
- Multi-layered: AI + security checks + quality heuristics
- Human-in-loop: Edge cases go to manual review queue
- Preventive: Catches issues before they're public
4. Full-Text Search with PostgreSQL
I needed semantic search without the complexity of vector embeddings. PostgreSQL's built-in full-text search is underrated:
-- Migration: Add full-text search indexes
CREATE INDEX templates_search_idx ON templates
USING GIN (to_tsvector('english', title || ' ' || content || ' ' || array_to_string(tags, ' ')));
-- Query with ranking
SELECT
*,
ts_rank(
to_tsvector('english', title || ' ' || content || ' ' || array_to_string(tags, ' ')),
plainto_tsquery('english', $1)
) AS rank
FROM templates
WHERE to_tsvector('english', title || ' ' || content || ' ' || array_to_string(tags, ' '))
@@ plainto_tsquery('english', $1)
ORDER BY rank DESC, created_at DESC
LIMIT 20;Features:
- Stemming: "running" matches "run", "ran", "runs"
- Stop words: Ignores "the", "a", "is" automatically
- Ranking: Results sorted by relevance + recency
- Performance: 20-50ms query time with proper indexes
- No external dependencies: Pure PostgreSQL, no Elasticsearch/Algolia
5. Tiptap Rich Text Editor for Email HTML
Email HTML has unique constraints - it needs to work across Gmail, Outlook, Apple Mail, and 20+ other clients with wildly different rendering engines. A standard WYSIWYG editor won't cut it. That's why emailhub.directory uses Tiptap with custom email-safe extensions.
Why Tiptap:
// components/tiptap/EmailEditor.tsx
const editor = useEditor({
extensions: [
StarterKit,
Table.configure({ resizable: true }),
Link.configure({ openOnClick: false }),
Placeholder.configure({ placeholder: 'Start typing your email...' }),
// Custom extension for email-safe HTML
EmailSafeHTML
],
content: initialContent,
onUpdate: ({ editor }) => {
// Convert to email-safe HTML
const html = sanitizeForEmail(editor.getHTML());
onChange(html);
}
});Email-specific features:
- Table support: Essential for email layouts (most email clients use table-based rendering)
- Inline CSS: Automatically converts classes to inline styles
- HTML sanitization: Strips unsupported tags (no
<div>in some clients) - Placeholder variables:
{{firstName}},{{companyName}}for personalization - Live preview: Shows how email renders in different clients
6. Self-Hosted Deployment with Coolify
Unlike most Next.js apps, I'm not on Vercel. I deployed to my own VPS using Coolify - an open-source, self-hosted Vercel alternative.
Why self-hosted:
- Cost: $20/month VPS vs $100+/month on Vercel at scale
- Control: Full access to logs, metrics, and infrastructure
- Learning: Understanding deployment from first principles
- No vendor lock-in: Can migrate anywhere
Deployment stack:
# .github/workflows/deploy.yml
name: Deploy to Production
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Build Docker image
run: docker build -t emailhub:latest .
- name: Deploy to Coolify
run: |
# Push to private registry
# Trigger Coolify webhook
# Zero-downtime deploymentBenefits:
- Automated deployments: Push to main → auto-deploy in 3 minutes
- Docker isolation: Reproducible builds, no "works on my machine"
- Health checks: Auto-rollback if new version fails
- SSL/HTTPS: Automatic Let's Encrypt certificates
- Database backups: Daily automated backups to S3
7. User Profiles with Follow System
Community platforms thrive on connections. I implemented a follow system using Supabase's relational queries:
// data/queries.ts
export async function getUserProfile(slug: string) {
const { data: user } = await supabase
.from('users')
.select(`
*,
followers:followers!follower_id(count),
following:followers!user_id(count),
templates:templates(count)
`)
.eq('slug', slug)
.single();
return user;
}
export async function followUser(targetUserId: string) {
const currentUser = await getCurrentUser();
await supabase
.from('followers')
.insert({
user_id: targetUserId,
follower_id: currentUser.id
});
// Send notification email (async)
await sendFollowNotification(targetUserId);
}Features:
- SEO-friendly URLs:
/u/philippekam - Follower/following counts
- Follow/unfollow actions
- Email notifications (optional)
- Activity feeds (planned)
Key Challenges & Solutions
Challenge 1: Email HTML is Not Web HTML
Problem: Email clients don't support modern CSS. No Flexbox, no Grid, no CSS variables.
Solution: Email-specific constraints
- Table-based layouts (the 1990s are back!)
- Inline CSS only (no external stylesheets)
- Limited font support (web-safe fonts only)
- Responsive =
max-width+ percentage widths - Test across 15+ email clients before shipping
Lesson: Build constraints into the editor, not documentation. The editor should make it impossible to create broken emails.
Challenge 2: Spam Prevention Without Friction
Problem: Open submission platforms attract spam, but requiring accounts kills growth.
Solution: Multi-layered defense
- Rate limiting (5 submissions/hour via Redis)
- AI moderation (auto-flag spam with OpenAI)
- Honeypot fields (hidden inputs that bots fill)
- Manual review queue (borderline cases reviewed by me)
- Account creation incentives (submit 3 templates → unlock profile)
Status: Moderation pipeline is working well, but I'm still tuning thresholds.
Challenge 3: Database Performance at Scale
Problem: Initial queries were slow (500ms+) as template count grew.
Solution: Strategic indexing
-- Index for search
CREATE INDEX templates_search_idx ON templates USING GIN (to_tsvector('english', title || ' ' || content));
-- Index for filtering
CREATE INDEX templates_category_idx ON templates(category);
CREATE INDEX templates_status_idx ON templates(status);
-- Composite index for sorting
CREATE INDEX templates_created_idx ON templates(created_at DESC);Result: Queries dropped to 20-50ms with proper indexes.
Challenge 4: Preview Accuracy
Problem: Showing users how their email will look in Gmail vs Outlook vs Apple Mail.
Solution: Multi-client simulation
- Sandboxed iframe with email client CSS resets
- CSS inliner (converts classes → inline styles)
- Show warnings for unsupported features
- Fallback to plaintext for problematic markup
Status: Preview is 80% accurate. Still working on Outlook rendering quirks.
What I'm Learning
1. Community platforms need patience Unlike SaaS where you can launch with core features, community platforms need critical mass. 50 templates isn't useful. 500 templates starts to be. I'm focusing on quality curation to bootstrap.
2. Self-hosting teaches fundamentals Deploying with Coolify forced me to understand Docker, reverse proxies, SSL certificates, and database backups. More work upfront, but deeper understanding.
3. Server components simplify SO much I initially over-engineered with client-side state management. Switching to server components eliminated 40% of my code. Less code = fewer bugs.
4. Type-safe server actions are a game changer Zod + next-safe-action caught injection attempts, malformed data, and edge cases I never would've thought of. The 30-minute setup saves hours of debugging.
5. AI moderation is a must-have, not nice-to-have I initially thought "I'll moderate manually". Terrible idea. Within days, spam attempts flooded in. OpenAI Moderation API ($0.002/check) is worth every penny.
6. Standing on the shoulders of giants works cursor.directory proved the community model works. I didn't reinvent the wheel - I adapted their proven patterns for email templates. No shame in learning from the best.
What's Next
I'm actively working on emailhub.directory features:
- Community voting system - Let users upvote the best templates
- Template collections - Curated bundles for common workflows (onboarding, sales, support)
- Email testing service - Test templates across 20+ clients automatically
- API access - Programmatic template fetching for developers
- Enhanced search - Filters by use case, tone, length
- Community features - Comments, favorites, sharing
Want to see these features? Join the community →
The honest truth: This is still early. I'm learning what works, what doesn't, and what the community actually needs. Building in public means sharing the messy middle, not just the polished end result.
Try It Yourself
Need email templates for your next project? Browse emailhub.directory - it's completely free with no signup required. Copy-paste ready HTML templates for cold outreach, customer onboarding, password resets, and more.
Whether you're a solo founder, developer, or marketer, you'll find templates that save hours of work. Start browsing templates →
If you're building a community platform or want to chat about Next.js 15, content moderation, or self-hosting, reach out on X or LinkedIn.
Building community platforms is hard but rewarding. It requires patience, quality focus, and a willingness to iterate based on user feedback. I'm excited to see where this goes.
If you're thinking about building a community platform, take inspiration from proven models like cursor.directory, focus on solving one problem really well, and don't be afraid to ship imperfect v1s.
Want to build your own community platform or SaaS? I'm available for consulting and freelance projects. Get in touch.