Best Boilerplates for Newsletter & Email Products 2026
Newsletter Platforms: Platform vs Custom
Newsletter businesses have exploded in 2024-2026. Substack's success spawned alternatives (Beehiiv, Kit formerly ConvertKit, Lemon Squeezy, Buttondown), each with different takes on monetization and design.
Build a custom newsletter platform when: you're building a platform for newsletter operators (like Beehiiv), your newsletter needs deep integration with other product features, or you need white-label email publishing.
Platform Decision
| Platform | Cost | Take Rate | Best For |
|---|---|---|---|
| Substack | Free | 10% | Discovery-focused newsletters |
| Beehiiv | $42-$99/mo | 0% | Scale-focused newsletters |
| Ghost | $9-$199/mo (self-host free) | 0% | Tech-savvy creators |
| Kit (ConvertKit) | $9+/mo | 0% | Marketing-heavy creators |
| Custom | Dev time | 0% | Platform builders |
For most newsletter creators: Ghost (self-hosted) or Beehiiv. Build custom only when you're building the platform itself.
Ghost — Best Open Source Newsletter Platform
Price: Free (self-host) / $9-$199/month (Ghost Pro) | Creator: Ghost Foundation
Ghost 5.x has excellent newsletter functionality: subscriber segments, scheduled sends, email analytics (open rate, click rate), paid memberships (Stripe), and portal widget for embeds.
# Self-host Ghost on Railway (cheapest managed option)
# Cost: ~$5/month (Railway Hobby plan)
ghost install --db sqlite3 # SQLite for small newsletters
# or
ghost install --db mysql # MySQL for larger newsletters
Email delivery: Ghost uses Mailgun by default. Budget: ~$15/month for 50k sends.
Building a Custom Newsletter Platform
For building a newsletter product (not just running a newsletter):
// Core newsletter data model
model Newsletter {
id String @id @default(cuid())
slug String @unique
name String
description String?
owner User @relation(fields: [ownerId], references: [id])
ownerId String
subscribers Subscriber[]
posts Post[]
// Stripe for paid subscriptions
stripeProductId String?
stripePriceId String?
}
model Subscriber {
id String @id @default(cuid())
email String
newsletter Newsletter @relation(fields: [newsletterId], references: [id])
newsletterId String
status SubscriberStatus @default(ACTIVE) // ACTIVE, UNSUBSCRIBED, BOUNCED
tier String @default("free") // free, paid
stripeSubId String? // If paid subscriber
openCount Int @default(0)
clickCount Int @default(0)
joinedAt DateTime @default(now())
@@unique([email, newsletterId])
}
Email Sending Infrastructure
For a newsletter platform, you need a transactional email provider that supports:
- High volume (millions of sends per month)
- Domain verification (DKIM, SPF, DMARC)
- Bounce handling (auto-unsubscribe hard bounces)
- Unsubscribe processing (honor unsubscribes automatically)
Best providers for newsletter platforms:
- Resend — Best developer experience, $20/month for 100k emails
- AWS SES — Cheapest at scale, $0.10 per 1000 emails
- Postmark — Best deliverability reputation, $15/month for 10k emails
- Mailgun — Good balance, $35/month for 100k emails
// Send newsletter issue via Resend
import { Resend } from 'resend';
const resend = new Resend(process.env.RESEND_API_KEY);
// Batch send (respect rate limits)
async function sendIssue(issueId: string) {
const issue = await db.post.findUnique({
where: { id: issueId },
include: { newsletter: { include: { subscribers: { where: { status: 'ACTIVE' } } } } },
});
// Send in batches of 100
const batches = chunk(issue.newsletter.subscribers, 100);
for (const batch of batches) {
await resend.batch.send(
batch.map(subscriber => ({
from: `${issue.newsletter.name} <newsletter@yourdomain.com>`,
to: subscriber.email,
subject: issue.title,
html: renderEmailTemplate(issue, subscriber),
headers: {
'List-Unsubscribe': `<https://yoursite.com/unsubscribe/${subscriber.id}>`,
'List-Unsubscribe-Post': 'List-Unsubscribe=One-Click',
},
}))
);
await sleep(1000); // Respect rate limits
}
}
Compare newsletter and SaaS boilerplates on StarterPick.
Check out this boilerplate
View Ghost on StarterPick →