Open Source

Ship email courses
from markdown.

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.

terminal

$ 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

What happens after someone subscribes

Every subscription follows the same lifecycle. No configuration needed — this is how ContentDrip works out of the box.

subscription lifecycle

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

Everything you need, nothing you don't.

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

From repo to running course in four steps.

01

Clone

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.

02

Write

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.

03

Deploy

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.

04

Drip

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

Markdown in, emails out.

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.

structure
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.md
pack.ts
const 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);
emails/day-1.md
---
subject: "Day 1: Getting
  Started"
preview: "Your first
  lesson awaits"
---

Good morning!

Today we're covering...

![hero](https://...)

## 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

Tools you already know.

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

Start building.

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.