Skip to content

akcho/milkie

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Milkie

npm version license

Drop-in paywall infrastructure for Next.js apps

Add Stripe subscriptions in minutes. Works with any auth provider (NextAuth, Clerk, Lucia, Supabase, etc.).

<MilkieProvider email={session.user.email}>
  <PaywallGate>
    <PremiumContent />
  </PaywallGate>
</MilkieProvider>

That's it. Your content is now behind a paywall.


✨ Features at a Glance

  • πŸ”’ Subscription Gating - <PaywallGate> for premium content
  • πŸšͺ Auth Gating - <AuthGate> for authenticated-only content
  • 🎨 Fully Customizable - Custom UIs, messaging, styling
  • πŸ” Security Built-In - Callback validation, idempotency, PII sanitization
  • πŸ”Œ Auth-Agnostic - Works with NextAuth, Clerk, Lucia, Supabase, etc.
  • ⚑ 3 API Routes - That's all you need on the backend

πŸš€ Try the Live Demo

milkie.dev

  1. Sign in with Google
  2. Try the gated examples (Component Gating or Layout Gating)
  3. See the paywall with built-in checkout flow
  4. Subscribe with test card: 4242 4242 4242 4242
  5. Content unlocked!

Current Status

v0.4.0 - Production Ready βœ…

Fully functional:

  • βœ… Stripe checkout & subscription management
  • βœ… Real-time webhook handling
  • βœ… Auth-agnostic design (works with any auth provider)
  • βœ… Subscription gating with <PaywallGate>
  • βœ… Authentication gating with <AuthGate>
  • βœ… Component-level and layout-level gating patterns
  • βœ… Smart sign-in redirects with callback URLs
  • βœ… Fully customizable UIs, messaging, and styling
  • βœ… Built-in blurred content previews
  • βœ… Toast notifications for errors
  • βœ… Security features (callback validation, idempotency, PII sanitization)

On the roadmap:

  • 🏒 Multi-tenancy support
  • πŸ“Š Developer dashboard
  • πŸ”„ Webhook relay service for local development
  • 🎚️ Multiple subscription tiers

πŸ“¦ Installation

npm install milkie

⚑ Quick Start

3 steps to add paywalls:

  1. Create 3 API routes (guide)
  2. Wrap your app: <MilkieProvider email={session?.user?.email}>
  3. Gate content: <PaywallGate><PremiumContent /></PaywallGate>

See: QUICKSTART.md for detailed setup or try the demo


πŸ“š Documentation

Getting Started:

Implementation:


How It Works

Subscription Gating

Protect premium content behind a subscription paywall:

import { PaywallGate } from "milkie";

<PaywallGate>
  <PremiumContent />
</PaywallGate>;

What happens:

  • Unauthenticated users see a sign-in prompt
  • Authenticated users without subscription see the paywall with checkout
  • Subscribers see the content

Customization options:

<PaywallGate
  title="Unlock Premium Features"
  subtitle="Get access to all premium content"
  subscribeButtonLabel="Upgrade Now"
  showBlurredChildren={true} // Show blurred preview of content
  overlayClassName="items-start pt-20" // Custom positioning
/>

Authentication Gating

Require sign-in without requiring a subscription:

import { AuthGate } from "milkie";

<AuthGate>
  <AuthenticatedContent />
</AuthGate>;

What happens:

  • Unauthenticated users see a sign-in prompt
  • Authenticated users see the content (no subscription required)

Customization options:

<AuthGate
  title="Sign in to continue"
  subtitle="Access your account"
  signInButtonLabel="Sign In"
  showBlurredChildren={false} // No blur, just show the overlay
/>

Custom Logic with Hooks

For complete control over your paywall logic:

import { usePaywall } from "milkie";

function CustomComponent() {
  const { hasAccess, loading, status, email } = usePaywall();

  if (loading) return <LoadingSpinner />;
  if (!hasAccess) return <YourCustomPaywall />;

  return <PremiumContent />;
}

Available from the hook:

  • hasAccess - boolean indicating subscription status
  • loading - boolean for loading state
  • status - Stripe subscription status string
  • email - user email from provider
  • error - error message if any
  • checkSubscription() - manually refresh subscription status
  • clearError() - clear error state

🎨 Customization

Milkie is designed to be fully customizable to match your app's design.

Custom Messaging

Tailor the paywall messaging to your brand:

<PaywallGate
  title="Upgrade to Pro"
  subtitle="Get unlimited access to all features"
  subscribeButtonLabel="Start Free Trial"
  signInButtonLabel="Sign in to subscribe"
/>

Custom Overlay Position

Position the paywall card anywhere on the page:

// Top of page
<PaywallGate overlayClassName="items-start pt-20" />

// Bottom of page
<PaywallGate overlayClassName="items-end pb-20" />

// Left side
<PaywallGate overlayClassName="justify-start pl-20" />

The overlayClassName accepts any Tailwind CSS classes. The overlay is placed in a CSS Grid container with items-center justify-items-center by default, so you can use alignment classes like items-start, items-end, justify-items-start, or justify-items-end to reposition it.

Fully Custom UI

Replace the entire paywall card with your own component:

import { usePaywall } from "milkie";

function YourCustomPaywall() {
  const { email, loading } = usePaywall();
  // Your custom logic and UI
}

<PaywallGate customUi={<YourCustomPaywall />} />;

With customUi, you have complete control over the paywall appearance and behavior.

See all customization options: packages/react/README.md


πŸ” Security Features

Milkie includes production-ready security protections:

  • Callback URL Validation - Prevents open redirect attacks by validating redirect URLs
  • Idempotency Keys - Prevents duplicate Stripe checkout sessions and charges
  • PII Sanitization - Removes sensitive data from error logs
  • Webhook Signature Verification - Validates Stripe webhook signatures
  • Email Normalization - Consistent email handling across the system

These security features are built-in and active by default.


Why Milkie?

Adding subscriptions shouldn't take 2 days. Here's what Milkie handles for you:

What you'd normally build:

  • ❌ Stripe checkout session creation
  • ❌ Webhook endpoint configuration
  • ❌ Subscription status tracking
  • ❌ Access control logic
  • ❌ Paywall UI components
  • ❌ Error handling and edge cases

With Milkie:

  • βœ… Wrap your content with <PaywallGate>
  • βœ… That's it

Time saved: Days β†’ Minutes


Tech Stack

Demo app:

  • Next.js 15 with App Router
  • TypeScript
  • NextAuth.js (Google OAuth)
  • Stripe Checkout & Webhooks
  • Drizzle ORM with PostgreSQL
  • Tailwind CSS + shadcn/ui
  • Sonner (toast notifications)

Package: (milkie)

  • Published on npm
  • React Context for state management
  • Factory functions for API routes
  • Database-agnostic adapters
  • TypeScript for type safety

Project Structure

milkie/
β”œβ”€β”€ LICENSE                     # MIT License
β”œβ”€β”€ README.md                   # You are here
β”œβ”€β”€ QUICKSTART.md               # Get running in 15 min
β”œβ”€β”€ packages/
β”‚   └── react/                  # milkie npm package (v0.1.0)
β”‚       β”œβ”€β”€ src/
β”‚       β”‚   β”œβ”€β”€ provider.tsx   # MilkieProvider & usePaywall hook
β”‚       β”‚   β”œβ”€β”€ paywall-gate/  # PaywallGate component
β”‚       β”‚   β”‚   └── components/
β”‚       β”‚   β”‚       β”œβ”€β”€ paywall-card.tsx
β”‚       β”‚   β”‚       β”œβ”€β”€ user-info.tsx
β”‚       β”‚   β”‚       └── checkout-error.tsx
β”‚       β”‚   β”œβ”€β”€ auth-gate/     # AuthGate component
β”‚       β”‚   β”‚   └── components/
β”‚       β”‚   β”‚       └── auth-card.tsx
β”‚       β”‚   β”œβ”€β”€ components/    # Shared components
β”‚       β”‚   β”‚   β”œβ”€β”€ loading-state.tsx
β”‚       β”‚   β”‚   β”œβ”€β”€ blurred-content.tsx
β”‚       β”‚   β”‚   β”œβ”€β”€ overlay-grid.tsx
β”‚       β”‚   β”‚   β”œβ”€β”€ milkie-icon.tsx
β”‚       β”‚   β”‚   └── ui/        # shadcn/ui components
β”‚       β”‚   └── api/           # Factory functions for routes
β”‚       β”‚       β”œβ”€β”€ subscription.ts  # createSubscriptionStatusRoute
β”‚       β”‚       β”œβ”€β”€ checkout.ts      # createCheckoutRoute
β”‚       β”‚       └── webhooks.ts      # createWebhookRoute
β”‚       └── README.md          # Package documentation
β”œβ”€β”€ docs/
β”‚   β”œβ”€β”€ BACKEND_SETUP.md        # Complete backend guide
β”‚   β”œβ”€β”€ AUTH_INTEGRATION.md     # Works with any auth
β”‚   β”œβ”€β”€ IMPLEMENTATION_GUIDE.md # Complete guide with 7 paywall patterns
β”‚   β”œβ”€β”€ paywall-patterns/       # Detailed pattern docs
β”‚   └── reference/              # API docs, customization, best practices
└── demo/                       # Working demo app
    β”œβ”€β”€ .env.example           # Environment variables template
    β”œβ”€β”€ app/                    # Next.js app directory
    β”‚   β”œβ”€β”€ page.tsx           # Public homepage
    β”‚   β”œβ”€β”€ mixed/             # Component-level gating example
    β”‚   β”œβ”€β”€ premium/           # Full-page gating example
    β”‚   β”œβ”€β”€ dashboard/         # Layout-level gating example
    β”‚   └── api/               # API routes (using milkie factories)
    β”‚       β”œβ”€β”€ checkout/      # Stripe checkout session
    β”‚       β”œβ”€β”€ subscription/  # Subscription status check
    β”‚       └── webhooks/      # Stripe webhook handler
    β”œβ”€β”€ lib/
    β”‚   β”œβ”€β”€ milkie-adapter.ts  # Database adapters for milkie
    β”‚   β”œβ”€β”€ db/                # Database schema & client
    β”‚   └── stripe.ts          # Stripe configuration
    └── auth.ts                # NextAuth configuration

Get Started

  1. Try the demo - See it in action
  2. Install the package - npm install milkie
  3. Set up backend - 3 API routes
  4. Integrate with your auth - Works with any provider
  5. Learn the patterns - Component gating, metered paywalls, custom checkout, and more

Feedback & Issues

This is an early-stage project. Feedback is incredibly valuable!

Open an issue if you:

  • Find bugs or unexpected behavior
  • Have feature requests or ideas
  • Want to discuss the implementation approach
  • Would actually use this for your projects

License

MIT