<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Adonai Dominguez</title>
    <description>The latest articles on DEV Community by Adonai Dominguez (@adonai_dominguez_f6c9b007).</description>
    <link>https://dev.to/adonai_dominguez_f6c9b007</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1493436%2F598089ae-c891-40d3-abe9-960953da53b0.jpg</url>
      <title>DEV Community: Adonai Dominguez</title>
      <link>https://dev.to/adonai_dominguez_f6c9b007</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/adonai_dominguez_f6c9b007"/>
    <language>en</language>
    <item>
      <title>I built a bio link tool that actually sells — here's the full stack behind SellBio</title>
      <dc:creator>Adonai Dominguez</dc:creator>
      <pubDate>Mon, 23 Mar 2026 19:32:27 +0000</pubDate>
      <link>https://dev.to/adonai_dominguez_f6c9b007/i-built-a-bio-link-tool-that-actually-sells-heres-the-full-stack-behind-sellbio-m4p</link>
      <guid>https://dev.to/adonai_dominguez_f6c9b007/i-built-a-bio-link-tool-that-actually-sells-heres-the-full-stack-behind-sellbio-m4p</guid>
      <description>&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;I wanted something different — a bio link that works like a sales funnel. So I built &lt;strong&gt;SellBio&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is SellBio?
&lt;/h2&gt;

&lt;p&gt;SellBio is a funnel/bio-link builder SaaS. Users create &lt;strong&gt;flows&lt;/strong&gt; (linear or branching screen sequences with lead capture, quizzes, VSLs) and a &lt;strong&gt;public store&lt;/strong&gt; (their bio-link page). Think Linktree meets ClickFunnels, but lightweight and designed for creators.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Live at:&lt;/strong&gt; &lt;a href="https://sellb.io" rel="noopener noreferrer"&gt;sellb.io&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here's what it looks like in action:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A creator puts their SellBio link in their Instagram bio&lt;/li&gt;
&lt;li&gt;A visitor taps the link and sees a clean bio page with links&lt;/li&gt;
&lt;li&gt;But instead of dead links, each one leads to a &lt;strong&gt;flow&lt;/strong&gt; — a sequence of screens that guides the visitor from curiosity to conversion&lt;/li&gt;
&lt;li&gt;The creator captures emails, qualifies leads with quizzes, shows VSLs, and closes sales — all from their bio link&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Tech Stack
&lt;/h2&gt;

&lt;p&gt;Here's everything under the hood:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Choice&lt;/th&gt;
&lt;th&gt;Why&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Framework&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Next.js 16 (App Router)&lt;/td&gt;
&lt;td&gt;Bleeding edge. Server components, no middleware.ts, Turbopack HMR&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Language&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;TypeScript 5 (strict)&lt;/td&gt;
&lt;td&gt;Non-negotiable for a SaaS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Styling&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Tailwind CSS v4 + shadcn/ui + Radix UI&lt;/td&gt;
&lt;td&gt;Fast iteration, accessible components out of the box&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Client State&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Zustand 5 + localStorage&lt;/td&gt;
&lt;td&gt;Simple, performant, persists draft state across navigation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Server State&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;TanStack Query&lt;/td&gt;
&lt;td&gt;Cache invalidation done right&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Forms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;React Hook Form + Zod v4&lt;/td&gt;
&lt;td&gt;Type-safe validation from form to API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Auth&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Better Auth 1.4&lt;/td&gt;
&lt;td&gt;Magic links, passkeys, OAuth — no Supabase Auth&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Database&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;PostgreSQL (Supabase)&lt;/td&gt;
&lt;td&gt;Raw SQL via &lt;code&gt;pg&lt;/code&gt;, no ORM. RLS on all user tables&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Payments&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Stripe (Billing + Connect)&lt;/td&gt;
&lt;td&gt;Subscriptions + creator payouts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Flow Builder&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;React Flow v12&lt;/td&gt;
&lt;td&gt;Visual node-based editor for advanced branching flows&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Rich Text&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;TipTap v3&lt;/td&gt;
&lt;td&gt;Screen content editing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Charts&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Recharts&lt;/td&gt;
&lt;td&gt;Conversion analytics dashboards&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Animations&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Framer Motion&lt;/td&gt;
&lt;td&gt;Smooth transitions between screens in flows&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;i18n&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;next-intl&lt;/td&gt;
&lt;td&gt;English + Spanish from day one&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Email&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Resend&lt;/td&gt;
&lt;td&gt;Transactional emails, magic links&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Linter&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Biome&lt;/td&gt;
&lt;td&gt;Fast, opinionated, replaces ESLint + Prettier&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Architecture Decisions That Mattered
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. No middleware.ts
&lt;/h3&gt;

&lt;p&gt;Next.js 16 dropped &lt;code&gt;middleware.ts&lt;/code&gt;. Instead of fighting it, I leaned in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Auth gating lives in a server-side utility (&lt;code&gt;coming-soon-gate.ts&lt;/code&gt;) applied inside layouts&lt;/li&gt;
&lt;li&gt;Domain redirects live in &lt;code&gt;next.config.ts&lt;/code&gt; &lt;code&gt;redirects()&lt;/code&gt; (production only)&lt;/li&gt;
&lt;li&gt;This ended up being cleaner than the middleware approach&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. app/ is routing-only
&lt;/h3&gt;

&lt;p&gt;This is the rule I'm most strict about: the &lt;code&gt;app/&lt;/code&gt; directory contains &lt;strong&gt;only&lt;/strong&gt; &lt;code&gt;page.tsx&lt;/code&gt;, &lt;code&gt;layout.tsx&lt;/code&gt;, &lt;code&gt;loading.tsx&lt;/code&gt;, and &lt;code&gt;route.ts&lt;/code&gt;. All logic, components, hooks, and utilities live outside in &lt;code&gt;components/&lt;/code&gt;, &lt;code&gt;lib/&lt;/code&gt;, and &lt;code&gt;contexts/&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;No &lt;code&gt;*Client.tsx&lt;/code&gt; wrappers in &lt;code&gt;app/&lt;/code&gt;. No business logic leaking into routing files. It keeps the codebase navigable even as it grows.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Better Auth over Supabase Auth
&lt;/h3&gt;

&lt;p&gt;I use Supabase for the database but &lt;strong&gt;not&lt;/strong&gt; for auth. Better Auth gave me:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Magic links + passkeys + OAuth in a single config&lt;/li&gt;
&lt;li&gt;Full control over session handling&lt;/li&gt;
&lt;li&gt;No cookie cache issues (disabled session cookie cache to prevent infinite reload loops — learned that one the hard way)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The API catch-all lives at &lt;code&gt;app/api/auth/[...all]/route.ts&lt;/code&gt; and every API route uses &lt;code&gt;auth.api.getSession()&lt;/code&gt; — never Supabase auth.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Raw SQL, No ORM
&lt;/h3&gt;

&lt;p&gt;Controversial, but I went with raw &lt;code&gt;pg&lt;/code&gt; 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.&lt;/p&gt;

&lt;p&gt;The schema lives in a single &lt;code&gt;supabase/01-schema.sql&lt;/code&gt; file. RLS is enabled on all user tables. Simple, auditable, fast.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Two Types of Flows
&lt;/h3&gt;

&lt;p&gt;SellBio supports two flow types:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Basic flows&lt;/strong&gt;: Linear screen sequences. Simple, fast to create. Think: lead magnet → form → thank you&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Advanced flows&lt;/strong&gt;: Built with React Flow v12. Full branching logic, conditional paths based on quiz answers, visual node editor&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The routing evaluator in &lt;code&gt;lib/utils/routing-evaluator.ts&lt;/code&gt; handles branch evaluation at runtime, resolving which screen to show next based on user responses.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Subscription Model
&lt;/h2&gt;

&lt;p&gt;I went with a freemium approach:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Starter (Free)&lt;/strong&gt;: 1 link, 1 flow, 3 screens, free design presets only&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pro&lt;/strong&gt;: Unlimited everything + premium themes + advanced analytics&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;14-day card-free trial&lt;/strong&gt; that auto-starts after onboarding&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The key architectural detail: card-free trials have &lt;code&gt;stripe_subscription_id = NULL&lt;/code&gt;. This distinguishes them from Stripe-managed subscriptions. A server-side guard (&lt;code&gt;lib/server/require-subscription.ts&lt;/code&gt;) checks trial expiry in real-time.&lt;/p&gt;

&lt;p&gt;A daily cron job (&lt;code&gt;/api/cron/trial-expiry&lt;/code&gt;) handles expiration + reminder emails via Resend.&lt;/p&gt;

&lt;p&gt;When users hit &lt;code&gt;trial_expired&lt;/code&gt;, &lt;code&gt;canceled&lt;/code&gt;, or &lt;code&gt;past_due&lt;/code&gt;, 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 &lt;code&gt;archived&lt;/code&gt; (hidden but preserved for reactivation).&lt;/p&gt;

&lt;h2&gt;
  
  
  The Design System
&lt;/h2&gt;

&lt;p&gt;Four free presets (&lt;code&gt;minimal&lt;/code&gt;, &lt;code&gt;ocean&lt;/code&gt;, &lt;code&gt;sunset&lt;/code&gt;, &lt;code&gt;forest&lt;/code&gt;) defined in &lt;code&gt;lib/design/themes.ts&lt;/code&gt;. A single function — &lt;code&gt;getThemeCssVariables(theme, mode)&lt;/code&gt; — generates all CSS variables and guarantees WCAG AA contrast ratios.&lt;/p&gt;

&lt;p&gt;Pro users get access to premium themes. The upgrade flow uses contextual modals (&lt;code&gt;UpgradeToProModal&lt;/code&gt; for design, &lt;code&gt;UpgradeToProFullModal&lt;/code&gt; for limits on screens/flows/links).&lt;/p&gt;

&lt;h2&gt;
  
  
  Draft State &amp;amp; UX Details
&lt;/h2&gt;

&lt;p&gt;Small things that make a big difference:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Newly created elements are &lt;strong&gt;immediately marked as saved&lt;/strong&gt; (no draft badge on first open)&lt;/li&gt;
&lt;li&gt;Draft state persists in localStorage — survives route changes&lt;/li&gt;
&lt;li&gt;Publishing requires clean state (&lt;code&gt;isDirty === false&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Back navigation always triggers &lt;code&gt;UnsavedChangesBackModal&lt;/code&gt; when dirty&lt;/li&gt;
&lt;li&gt;The screen builder has 3 stages: &lt;strong&gt;Config → Components → Preview&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What I'd Do Differently
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Start with i18n from day one&lt;/strong&gt; — I did, and it saved me weeks of refactoring. &lt;code&gt;next-intl&lt;/code&gt; with JSON message files is the way&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pick Biome over ESLint&lt;/strong&gt; — faster, simpler config, and it handles formatting too&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Don't use Supabase Auth if you need flexibility&lt;/strong&gt; — Better Auth was worth the setup cost&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Invest in a real design token system early&lt;/strong&gt; — the &lt;code&gt;getThemeCssVariables&lt;/code&gt; approach paid off massively when adding new themes&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;AI-powered flow suggestions based on user's niche&lt;/li&gt;
&lt;li&gt;A/B testing for screens within flows&lt;/li&gt;
&lt;li&gt;Stripe Connect for creators to sell directly through their flows&lt;/li&gt;
&lt;li&gt;More integrations (Calendly embeds, email providers)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try It
&lt;/h2&gt;

&lt;p&gt;If you're a creator, coach, or entrepreneur tired of bio links that don't convert — give SellBio a shot.&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://sellb.io" rel="noopener noreferrer"&gt;sellb.io&lt;/a&gt;&lt;/strong&gt; — 14-day free trial, no credit card required.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;If you found this useful, a ❤️ or 🦄 helps more people discover the post. Thanks for reading!&lt;/em&gt;&lt;/p&gt;

</description>
      <category>news</category>
      <category>startup</category>
      <category>showdev</category>
      <category>nextjs</category>
    </item>
  </channel>
</rss>
