Open Source
ContentDrip is an open-source Next.js template for building automated email drip courses. You write content in markdown, define a delivery schedule, and subscribers receive one lesson at a time — at the hour they choose, in their timezone.
$ git clone content-drip my-course
$ cd my-course && npm install
$ npm run dev
▸ Ready on localhost:3000
MIT Licensed · Self-Hosted · No Vendor Lock-In
The Drip Flow
Every subscription follows the same lifecycle. No configuration needed — this is how ContentDrip works out of the box.
01 subscribe ── visitor enters email + preferred delivery time
02 confirm ─── signed token email → click to activate
03 welcome ─── step 0 sent immediately on confirmation
04 drip ──── one lesson per day at the chosen time & timezone
05 complete ── subscription marked as completed after final step
at any point: pause ──→ resume exactly where they left off
at any point: unsubscribe ──→ one-click, signed link, instant
Signed Tokens
Every action link (confirm, manage, pause, stop) uses a cryptographically signed, single-use token. No passwords, no sessions.
Idempotent Delivery
The send log tracks every email sent. If the cron job runs twice, the same step is never sent again. Safe to retry, safe to overlap.
Status Machine
Subscriptions move through PENDING_CONFIRM → ACTIVE → PAUSED → COMPLETED or STOPPED. Every transition is logged and auditable.
Features
8 built-in
Content Packs
Each course is a self-contained pack — a folder of markdown emails, companion web pages, and a custom email template. Create one pack or twenty. They're independent, portable, and easy to reason about.
Scheduled Delivery
Subscribers choose the hour they want to receive emails. A cron job evaluates every active subscription each minute and sends emails at the exact time the subscriber chose. No batching, no approximation.
Timezone Aware
Timezones are auto-detected from the subscriber's browser via the Intl API and stored per subscription. The scheduler evaluates cron expressions in the subscriber's local timezone, so 8am means 8am wherever they are.
Companion Pages
Every email has a web-readable companion page at /p/{pack}/{step}. Subscribers can share links, read on any device, and access content without digging through their inbox. Pages are generated from markdown in your pack's pages/ directory.
Subscriber Management
Built-in manage page where subscribers update their delivery time, timezone, or unsubscribe — all via secure, signed, single-use token links. No account creation, no passwords. Just click the link in any email.
Pause & Resume
Subscribers pause delivery with one click from any email. When they resume, they pick up exactly where they left off — the scheduler tracks which step was last sent and continues from the next one.
Email Branding
Each pack defines its own EmailShell — a React Email component that wraps every outgoing email in custom branding. Headers, footers, typography, colors, images — full control over every pixel, tested across email clients.
One-Click Unsubscribe
Every email includes a signed, one-click unsubscribe link that immediately stops delivery. The link is unique per subscriber and cryptographically verified. CAN-SPAM and GDPR compliant out of the box.
How It Works
Fork or clone the ContentDrip template repository. It's a standard Next.js app with the App Router — no custom build tools, no exotic dependencies. If you've used Next.js before, you already know how this works.
Create a content pack by writing markdown files with YAML frontmatter for subjects and previews. Define your steps in pack.ts, build an EmailShell for branding, and register the pack. That's the entire content model.
Push to Vercel (or any Node.js host). Set your environment variables for the database (Turso), email provider (Postmark), and cron secret. Configure a cron job to hit /api/cron every minute. That's the infrastructure.
Visitors land on your pack's page, enter their email, and choose a delivery time. They confirm via email, get a welcome message immediately, then receive one lesson per day at their chosen time in their timezone.
Content Packs
A content pack is a folder. Inside: a config file, an email template, and markdown files for each lesson. ContentDrip reads the markdown, replaces template variables with signed subscriber URLs, renders it through your EmailShell, and sends it via Postmark.
src/content-packs/
└── my-course/
├── pack.ts
├── email-shell.tsx
├── emails/
│ ├── welcome.md
│ ├── day-1.md
│ ├── day-2.md
│ └── day-3.md
└── pages/
├── welcome.md
├── day-1.md
└── day-2.mdconst pack: ContentPack = {
key: "my-course",
name: "My Email Course",
description: "...",
steps: [
{ slug: "welcome",
emailFile: "welcome.md" },
{ slug: "day-1",
emailFile: "day-1.md" },
{ slug: "day-2",
emailFile: "day-2.md" },
{ slug: "day-3",
emailFile: "day-3.md" },
],
EmailShell: MyShell,
};
registerPack(pack);---
subject: "Day 1: Getting
Started"
preview: "Your first
lesson awaits"
---
Good morning!
Today we're covering...

## The Key Idea
Content goes here in
standard **markdown**.
[Read online →](
{{companionUrl}}
){{companionUrl}}Web version of this lesson
{{manageUrl}}Manage subscription page
{{pauseUrl}}Pause delivery one-click
{{stopUrl}}Unsubscribe one-click
Stack
Next.js
App Router, Server Actions, API Routes
React Email
Type-safe, cross-client email templates
Drizzle ORM
Type-safe SQL with zero-overhead
SQLite / Turso
Edge-ready, zero-config database
Postmark
Transactional email with delivery tracking
Tailwind CSS
Utility-first styling throughout
Clone the repo, create a content pack in markdown, deploy to Vercel, and your subscribers start learning. The whole setup takes minutes, not weeks. Read the docs or explore the live example.