Drag-and-drop page builders, custom domains, click tracking, and the monetization features that let you charge $29/month while Linktree gives away the basics.
Research-based methodology. This guide synthesizes Vercel and Cloudflare custom-domain docs, public Linktree/Beacons writeups, dnd-kit documentation, and our own builds with Claude. Where we have first-person experience we say so; otherwise we’re working from public sources. How we research.
Link-in-bio looks like a solved category. Linktree has tens of millions of users, Beacons raised serious money, and Instagram lets creators add multiple links natively now. So the obvious read is: don’t bother. The non-obvious read is that the underlying product is a 200-line Tailwind page builder, and the entire market is still won on (a) creator monetization features, (b) custom-domain ergonomics, and (c) niche fit. There’s a reason new entrants ship every quarter.
This guide is for someone who wants to ship a real, paid link-in-bio product in 3–6 weeks using Claude as their build partner. We’ll skip the obvious (a stack of buttons on a page) and focus on the load-bearing parts: the visual editor, custom-domain SSL, click analytics that survive bot traffic, and the monetization features that justify charging more than zero. If you want the broader build playbook first, our How to build a SaaS with Claude guide covers the general scaffolding workflow this one builds on top of.
Linktree is the default. Calling them out as a competitor is honest. So is the path around them. Four real wedges in 2026:
Pick one. Talk to ten target creators before you write a line of code. Build the public-page render first, the editor second, the analytics third.
The data model is small. The hard part is slug uniqueness across users, custom domains, and reserved words (your /about page can’t collide with a user named about).
I'm building a link-in-bio SaaS for [podcasters / fitness creators / B2B founders]. The model needs to support: - Users (creators) with one or more pages - Each page has a unique slug used in the URL: /username - Each page has a list of links (ordered, drag-reorderable) - Each link has a type: url, email, phone, paid_product, tip_jar, embed (youtube, spotify, podcast) - Each page can have a theme (preset or custom) - Each page can optionally bind to a custom domain - We track clicks per link with timestamp, country, referrer, device - Reserved slugs (about, login, api, admin, settings, dashboard, etc.) must not be claimable Design the complete Postgres schema. For every table give me: - Columns with types and constraints - A `slug` uniqueness approach: case-insensitive, ASCII-folded, with a reserved_slugs blocklist table - An ordering strategy for the links on a page (fractional indexing) - Indexes for: load all links for a page (the hottest read query), aggregate clicks per link per day for analytics Then write Supabase RLS so: - A user can only edit their own pages and links - The public render endpoint reads pages without auth (use a security- definer function, not RLS bypass) - Reserved slug enforcement is in a check constraint or a trigger Output as one SQL file I can run in the Supabase SQL editor.
Two non-obvious bits Claude will likely miss on the first pass: store the slug as citext (case-insensitive text) with a unique index, and validate against a reserved_slugs table at insert time so you can update the blocklist without redeploying. The reserved list will grow — you’ll add words after the first time someone claims /api and breaks your routing.
This is where you out-build Linktree. The editor must feel instant. Drag a link, see the preview update in real time. Change a theme color, see the page reflow. Reorder by dragging the handle, no save button. Every interaction zero-latency.
Use dnd-kit for the React drag-and-drop primitives — it’s the modern default, well-maintained, and supports keyboard accessibility out of the box. react-beautiful-dnd is unmaintained; avoid.
Build a split-pane page editor in Next.js: LEFT PANE (editor, 50% width): - A list of links rendered as cards, each draggable via dnd-kit - Each card has: an icon picker, a label input, a URL input (or type-specific fields for embeds/products), a toggle for visible/hidden, and a delete button - A "+ Add link" button at the bottom that opens a type picker (URL, Email, YouTube embed, Spotify embed, Tip Jar, Paid Product) - Drag handle on the left of each card; drag updates the position field via fractional indexing and persists to Supabase on drop - Theme picker section with preset themes and custom color overrides (background, text, button bg, button text) RIGHT PANE (live preview, 50% width): - An iframe-free render of the public page using the same component as the live URL would render - Subscribes to the editor state via Zustand or React context so changes are instant - Shows a phone-frame mockup with the page inside, since 90%+ of bio page views happen on mobile Auto-save: - Every change to a link or theme triggers a debounced save (500ms) to Supabase - Show a tiny "saved" indicator next to the editor title - On network failure, queue the change locally and retry Output: the EditorPage component, the LinkCard component, the theme picker, the live-preview component, and the Zustand store.
One detail to insist on: keyboard accessibility. dnd-kit supports it — arrow keys to reorder, space to pick up, escape to cancel. Most of your competitors drop this. Creators with motor accessibility needs will notice and tell their friends.
Custom domains are the single most-requested upgrade feature in this category. They’re also the feature most likely to break in production. The mechanics: a creator owns links.theirpodcast.com, points it at your service via a CNAME, and your edge serves their page at that domain with valid SSL.
The two reasonable architectures: (a) Vercel’s domains API on top of a Next.js app, where Vercel issues SSL via Let’s Encrypt automatically, or (b) Cloudflare for SaaS, where Cloudflare is the edge and you proxy requests to your origin. We default to Vercel for solo founders because it’s already where you deploy.
I'm using Vercel and Next.js (App Router) middleware to map custom
domains to user pages.
Build the full custom-domain flow:
1. UI: in the dashboard, a creator on the Pro plan adds a custom domain.
They enter "links.theirpodcast.com". I show them:
- Their CNAME target: cname.vercel-dns.com
- A "Verify" button that polls until DNS propagates
2. Server: when they click Verify, hit the Vercel domains API to add the
domain to my project. Store the domain row with status='pending'.
3. Polling: a server route /api/domains/[id]/check that:
- Resolves the domain via DNS-over-HTTPS (cloudflare-dns.com)
- Confirms the CNAME points at cname.vercel-dns.com
- If yes, calls Vercel's verify endpoint and updates status='verified'
- If no, returns the current state with helpful next-step text
4. Middleware (middleware.ts): on every request, look up the host header.
If it's a custom domain in my domains table, rewrite the URL to
/[username] internally (preserve the path).
5. SSL: Vercel auto-issues Let's Encrypt for verified custom domains.
Surface that status in the UI ("SSL active" / "issuing...").
6. Removal: when a creator removes a domain or downgrades, call Vercel
to detach and soft-delete my row.
Handle edge cases: subdomain vs apex (apex needs A record not CNAME),
already-verified-on-another-account error, rate limits on Vercel API.
The non-obvious failure mode: someone tries to add the apex of a domain (theirpodcast.com) instead of a subdomain. Apex domains need an A record, not a CNAME, and Vercel’s targets differ. Detect this case in the UI and route them to the right instructions or you’ll get five support emails a week.
Click analytics is the second most-requested feature after custom domains. Creators want to see which links convert, which posts drive the most traffic, and which countries their audience comes from. The easy version of click tracking is broken in two ways: it counts bot traffic and it slows down the redirect.
Build a click-tracking system for my link-in-bio SaaS.
Architecture:
- Public page renders links as <a href="/r/[link_id]"> (redirect URL)
- /r/[link_id] is an Edge route (Vercel Edge Runtime) that:
1. Looks up the destination URL by link_id (use Vercel KV or
Supabase with edge-compatible client — recommend the better one
for sub-50ms edge response)
2. Issues a 302 redirect to the destination IMMEDIATELY
3. Fires a fire-and-forget log to a queue (do NOT await before redirect)
Logging worker:
- Reads from the queue and writes click rows to Supabase
- For each click, capture: link_id, timestamp, ip-derived country (from
x-vercel-ip-country header), user-agent, referrer
- Bot filtering: maintain a regex list of known bots (Googlebot, Bingbot,
curl, wget, Slackbot, Twitterbot, etc.) and tag those rows as
is_bot=true rather than dropping them (you want the data for debugging
but exclude from creator-facing counts)
- Rate-limit by IP: more than 30 clicks/minute from one IP gets tagged
as suspected_bot=true
Analytics queries:
- Clicks per link per day (creator dashboard)
- Top countries
- Top referrers (exclude direct/empty)
- Conversion rate (clicks / page views)
Build the Edge route, the queue consumer, and the SQL for the analytics
queries. Performance target: redirect under 80ms p95.
The fire-and-forget pattern is the difference between “link-in-bio that feels native” and “link-in-bio that lags every click.” If your redirect waits for a database write, p95 becomes 300ms+ on cold connections and creators notice. PostHog is a reasonable alternative if you want to outsource the analytics layer entirely — their $0 tier handles a lot of clicks before you pay anything.
Themes are how creators make a page feel like theirs. Every link-in-bio competitor handles this badly: either they ship 5 hardcoded themes (boring) or they expose 30 CSS properties through a forms UI (overwhelming). The right pattern is a small set of design tokens exposed as CSS variables, with presets and a custom override panel.
Design a theme system for my link-in-bio public pages.
Structure:
1. A `themes` table with preset themes (id, name, tokens jsonb)
2. A `pages` table that stores either a theme_id (using a preset) or
custom_tokens jsonb (overriding the preset)
Token shape (the single source of truth for both presets and custom):
{
"color_bg": "#0f0f0f",
"color_text": "#ffffff",
"color_button_bg": "#7FD4A8",
"color_button_text": "#0f0f0f",
"color_button_border": "transparent",
"font_family": "'Inter', system-ui, sans-serif",
"button_radius": "12px",
"button_style": "solid", // solid | outline | soft
"background_style": "solid" // solid | gradient | image
}
Render flow:
- The public page server-component reads the page's theme_id, loads the
preset, merges custom_tokens on top
- Outputs a <style> block at the top of the page that sets these as
CSS variables on :root, scoped to the page
- The Tailwind classes on links use bg-[var(--color-button-bg)] etc.
Editor flow:
- Theme picker shows preset thumbnails (rendered with each theme's tokens
via the same component — no separate preview rendering)
- "Customize" reveals a form with color pickers and selects bound to
the same token shape
- Live preview reflects changes immediately via a Zustand store
Output: the Theme TS type, the merge utility, the public-page renderer,
the editor's theme picker, and 6 starter preset themes (Dark, Light,
Midnight, Sunrise, Forest, Plum).
One opinionated take: don’t let creators upload arbitrary CSS. It’s a security risk (CSS keyloggers exist), it’s a support nightmare (broken layouts), and it doesn’t make pages look better — most creators with custom CSS make their page worse. Cap them at the token shape and ship more presets.
The freemium math for link-in-bio is well-understood. Linktree converts ~3% of free users to paid. Beacons aims higher with monetization-first positioning. As a solo founder you need conversion above 5% to make the numbers work.
For monetization features you’ll need a payment processor. Lemon Squeezy vs Stripe matters here: if creators are selling digital products, Lemon Squeezy as merchant-of-record handles VAT/sales tax for you. If creators are selling services or physical goods, Stripe Connect with each creator as their own merchant is the better fit.
Creator-side, your real competitive moat is integrations. A podcaster who plugs in their RSS feed and sees their latest episode auto-update on their bio page churns at half the rate of one who has to manually update a link every time. Browse our list of micro SaaS ideas for niche-creator wedges still wide open. If you’re bundling email capture into the page (a common ask), our Beehiiv vs Substack piece covers what to integrate with.
A link-in-bio that targets one creator vertical, treats monetization as the primary action, and ships a live-preview drag-and-drop editor with seamless custom domains is a real $5K–$15K MRR business. Generic link-in-bio is dead. Pick a niche, talk to ten creators, ship the public page render first.
The stack, prompts, pricing, and mistakes to avoid — for solo founders building with AI.