Create, publish, and manage lead magnets that actually convert.
A full-stack SaaS for building interactive lead magnets, capturing leads through public share pages, and turning them into customers — with built-in CRM, analytics, and billing.
- Visual form builder — design lead magnets with a drag-and-drop form builder, custom fields, and validation.
- Public share pages — every lead magnet gets a hosted page (
/v/[slug]) with smart deduplication to avoid duplicate leads. - Built-in CRM — collect, search, filter, and qualify leads in a dedicated workspace.
- Analytics — track views, submissions, and conversion over time.
- Subscriptions & billing — Stripe-powered plans (Free / Pro) with usage limits and customer portals.
- Email notifications — transactional emails (verification, password reset, OTP, welcome) via Resend.
- Auth — email/password with verification, OTP, and Google OAuth (better-auth).
- Asset storage — screenshots and uploads served from Cloudflare R2.
| Landing & form | Dashboard & CRM | Analytics |
|---|---|---|
![]() |
![]() |
![]() |
![]() |
![]() |
| Layer | Technology |
|---|---|
| Framework | Next.js 15 (App Router, React 19) |
| API | tRPC + React Query |
| Database | PostgreSQL via Drizzle ORM |
| Auth | better-auth |
| Billing | Stripe |
| Resend | |
| Storage | Cloudflare R2 |
| Jobs | Trigger.dev |
| Styling | Tailwind CSS v4 + shadcn/ui |
| Deploy | Cloudflare via OpenNext |
The codebase follows a feature-first, module-based architecture. Server logic is split into thin tRPC procedures, business services, and Drizzle repositories, with input validation centralized in Zod DTOs.
src/
├─ app/ # Next.js routes: (public), (auth), (private), api
├─ components/ # Shared UI (shadcn-style) and marketing sections
├─ db/ # Drizzle client and schema
├─ lib/ # Auth client/server, shared utilities
├─ modules/ # Feature modules (auth, lead-magnets, viewer, analytics,
│ # subscription, email, settings)
│ └─ <feature>/
│ ├─ server/ # procedures.ts, service.ts, repository.ts, dto.ts
│ ├─ components/ # Feature UI
│ └─ types/ # Shared types
├─ trpc/ # tRPC client/server setup
└─ trigger/ # Background jobs
- Node.js 20+
- pnpm 10+
- A PostgreSQL database (e.g. Supabase)
- Accounts/keys for Stripe, Resend, Google OAuth, and Cloudflare R2 (see
.env.example)
# Install dependencies
pnpm install
# Copy and fill in environment variables
cp .env.example .env.local
# Apply database migrations
pnpm dlx drizzle-kit push
# Run the development server
pnpm devOpen http://localhost:3000.
Note
drizzle.config.ts reads .env.development (or .env.local) for DATABASE_URL. Adjust the path there if your local env file is named differently.
All configuration is provided through environment variables. See .env.example for the full list and descriptions. Never commit real secrets — only .env.example is tracked.
| Script | Description |
|---|---|
pnpm dev |
Start the Next.js dev server |
pnpm build |
Build the production app |
pnpm lint |
Run ESLint |
pnpm test |
Run the Jest test suite |
pnpm preview |
Build and preview the Cloudflare deployment locally |
pnpm deploy |
Build and deploy to Cloudflare |
This app is deployed to Cloudflare using OpenNext for Cloudflare. Bindings (D1/Postgres, R2, secrets) are configured in wrangler.jsonc.
pnpm deploySet the same environment variables from .env.example as Cloudflare secrets / vars before deploying.
- Secrets are loaded from environment variables only — none are hardcoded in the source.
- Authentication and session management are handled by better-auth; all protected procedures verify the session server-side (
protectedProcedure). - Data access is scoped per-user at the service/repository layer (ownership checks on every query).
- Security headers (HSTS,
X-Content-Type-Options,X-Frame-Options,Referrer-Policy,Permissions-Policy) are set globally innext.config.ts. - Public share pages only expose lead magnets explicitly marked active, and never reveal other users' data.




