Most bio link tools are just... lists of links. You click, you leave, nothing happens. No lead capture, no funnels, no analytics that actually matter.
I wanted something different — a bio link that works like a sales funnel. So I built SellBio.
What is SellBio?
SellBio is a funnel/bio-link builder SaaS. Users create flows (linear or branching screen sequences with lead capture, quizzes, VSLs) and a public store (their bio-link page). Think Linktree meets ClickFunnels, but lightweight and designed for creators.
Live at: sellb.io
Here's what it looks like in action:
- A creator puts their SellBio link in their Instagram bio
- A visitor taps the link and sees a clean bio page with links
- But instead of dead links, each one leads to a flow — a sequence of screens that guides the visitor from curiosity to conversion
- The creator captures emails, qualifies leads with quizzes, shows VSLs, and closes sales — all from their bio link
The Tech Stack
Here's everything under the hood:
| Layer | Choice | Why |
|---|---|---|
| Framework | Next.js 16 (App Router) | Bleeding edge. Server components, no middleware.ts, Turbopack HMR |
| Language | TypeScript 5 (strict) | Non-negotiable for a SaaS |
| Styling | Tailwind CSS v4 + shadcn/ui + Radix UI | Fast iteration, accessible components out of the box |
| Client State | Zustand 5 + localStorage | Simple, performant, persists draft state across navigation |
| Server State | TanStack Query | Cache invalidation done right |
| Forms | React Hook Form + Zod v4 | Type-safe validation from form to API |
| Auth | Better Auth 1.4 | Magic links, passkeys, OAuth — no Supabase Auth |
| Database | PostgreSQL (Supabase) | Raw SQL via pg, no ORM. RLS on all user tables |
| Payments | Stripe (Billing + Connect) | Subscriptions + creator payouts |
| Flow Builder | React Flow v12 | Visual node-based editor for advanced branching flows |
| Rich Text | TipTap v3 | Screen content editing |
| Charts | Recharts | Conversion analytics dashboards |
| Animations | Framer Motion | Smooth transitions between screens in flows |
| i18n | next-intl | English + Spanish from day one |
| Resend | Transactional emails, magic links | |
| Linter | Biome | Fast, opinionated, replaces ESLint + Prettier |
Architecture Decisions That Mattered
1. No middleware.ts
Next.js 16 dropped middleware.ts. Instead of fighting it, I leaned in:
- Auth gating lives in a server-side utility (
coming-soon-gate.ts) applied inside layouts - Domain redirects live in
next.config.tsredirects()(production only) - This ended up being cleaner than the middleware approach
2. app/ is routing-only
This is the rule I'm most strict about: the app/ directory contains only page.tsx, layout.tsx, loading.tsx, and route.ts. All logic, components, hooks, and utilities live outside in components/, lib/, and contexts/.
No *Client.tsx wrappers in app/. No business logic leaking into routing files. It keeps the codebase navigable even as it grows.
3. Better Auth over Supabase Auth
I use Supabase for the database but not for auth. Better Auth gave me:
- Magic links + passkeys + OAuth in a single config
- Full control over session handling
- No cookie cache issues (disabled session cookie cache to prevent infinite reload loops — learned that one the hard way)
The API catch-all lives at app/api/auth/[...all]/route.ts and every API route uses auth.api.getSession() — never Supabase auth.
4. Raw SQL, No ORM
Controversial, but I went with raw pg queries instead of Prisma/Drizzle. For a product where I need full control over query performance and Supabase's transaction-mode pooler (port 6543), raw SQL was the right call.
The schema lives in a single supabase/01-schema.sql file. RLS is enabled on all user tables. Simple, auditable, fast.
5. Two Types of Flows
SellBio supports two flow types:
- Basic flows: Linear screen sequences. Simple, fast to create. Think: lead magnet → form → thank you
- Advanced flows: Built with React Flow v12. Full branching logic, conditional paths based on quiz answers, visual node editor
The routing evaluator in lib/utils/routing-evaluator.ts handles branch evaluation at runtime, resolving which screen to show next based on user responses.
The Subscription Model
I went with a freemium approach:
- Starter (Free): 1 link, 1 flow, 3 screens, free design presets only
- Pro: Unlimited everything + premium themes + advanced analytics
- 14-day card-free trial that auto-starts after onboarding
The key architectural detail: card-free trials have stripe_subscription_id = NULL. This distinguishes them from Stripe-managed subscriptions. A server-side guard (lib/server/require-subscription.ts) checks trial expiry in real-time.
A daily cron job (/api/cron/trial-expiry) handles expiration + reminder emails via Resend.
When users hit trial_expired, canceled, or past_due, they see a non-closeable paywall modal. If they downgrade to Starter and have content exceeding limits, they choose what to keep — the rest gets archived (hidden but preserved for reactivation).
The Design System
Four free presets (minimal, ocean, sunset, forest) defined in lib/design/themes.ts. A single function — getThemeCssVariables(theme, mode) — generates all CSS variables and guarantees WCAG AA contrast ratios.
Pro users get access to premium themes. The upgrade flow uses contextual modals (UpgradeToProModal for design, UpgradeToProFullModal for limits on screens/flows/links).
Draft State & UX Details
Small things that make a big difference:
- Newly created elements are immediately marked as saved (no draft badge on first open)
- Draft state persists in localStorage — survives route changes
- Publishing requires clean state (
isDirty === false) - Back navigation always triggers
UnsavedChangesBackModalwhen dirty - The screen builder has 3 stages: Config → Components → Preview
What I'd Do Differently
-
Start with i18n from day one — I did, and it saved me weeks of refactoring.
next-intlwith JSON message files is the way - Pick Biome over ESLint — faster, simpler config, and it handles formatting too
- Don't use Supabase Auth if you need flexibility — Better Auth was worth the setup cost
-
Invest in a real design token system early — the
getThemeCssVariablesapproach paid off massively when adding new themes
What's Next
- AI-powered flow suggestions based on user's niche
- A/B testing for screens within flows
- Stripe Connect for creators to sell directly through their flows
- More integrations (Calendly embeds, email providers)
Try It
If you're a creator, coach, or entrepreneur tired of bio links that don't convert — give SellBio a shot.
👉 sellb.io — 14-day free trial, no credit card required.
I'd love feedback from the dev community. What would you build differently? What's missing from the stack? Drop a comment — I read every one.
If you found this useful, a ❤️ or 🦄 helps more people discover the post. Thanks for reading!
Top comments (0)