MyCliq.co
2025
MyCliq
Complete Brand & Platform Development
MyCliq.co is a real-time website builder for creatives. On a single page, an artist can showcase their work, link every social handle they have, and sell products directly to the people who find them.
It is a complete end-to-end software product, currently in production with 1,000+ users, and the entire ecosystem was solo developed from concept to completion. This undertaking encompassed complete brand development, 3D and 2D commercial production, full-stack website development, and native software platform creation for desktop and mobile devices.
Starting from a blank canvas, I crafted every aspect of the product: front-end and backend architecture, complete with full documentation and video tutorials. Includes advanced integrations with Stripe, Gumroad, Cloudflare, Apple Music, Spotify, YouTube, and Vimeo.
This case study runs long, and it is already cut down. Depending on which part of the project you came here for, you can jump straight to it:
Product Design
The Product
Creatives are usually told to solve the same problem three times over: a portfolio site to show the work, a link-in-bio service to hold the social handles, and a separate storefront to sell anything. Three subscriptions, three places to keep updated, and an audience that has to be sent somewhere else at every step.
MyCliq collapses all of it into one page. Work, social handles, and products live together, and the builder renders that page in real time as it is being made. There is no preview mode to toggle and no publish step to wait on. What the creator sees while building is exactly what a visitor sees.
The dashboard is home base. Creators manage every page they have published, swap out their profile picture, and preview the live page exactly as a visitor will see it, without ever leaving the screen.
Analytics show how a page is actually performing: how many people visited, how many opened each link, and who viewed which products. How deep that reporting goes scales with the creator's subscription tier.
The live builder puts nearly every touchpoint of the page under the creator's control: size, spacing, arrangement, color, link icons, backgrounds, tags, text effects, and subpages, a page nested inside a page. Every adjustment renders on the real page as it is made.
The animated 3D icon library is the heart of MyCliq. Every icon was modeled and animated from scratch by hand, over 520 of them, and they exist nowhere else. No other platform can offer them, which makes a MyCliq page recognizable at a glance.
Creators connect their MyCliq account to Stripe and start selling their services straight from the page. Connecting Gumroad does the same for digital products, sold natively without sending anyone to a separate storefront.
Creators can upload their own photography or pull from a library of more than 200 animated looping backgrounds. I produced about half of them myself. The rest are licensed to MyCliq.co through its artist community network, which keeps new work arriving without any one person having to make all of it.
Auth
App Icon
Anywhere
MyCliq runs in the browser, but it does not have to stay there. Both the real-time builder and the live MyCliq page install to a phone homescreen as a progressive web app, opening full screen with no address bar and behaving like native software. A creator can edit their page from an icon on their homescreen, and a fan can keep that creator's page there the same way.
That matters for the audience as much as it does for the creator. A link dropped in a story gets opened once. An icon sitting on a homescreen gets opened again.
In Person
It also doubles as a digital business card. A paper card constrains a person to a job title, an email address, and a phone number, and almost none of that says anything about what they actually make. At a conference or across a table, that is the moment where the work matters most and is hardest to show.
MyCliq shows the whole picture in one tap: the work itself, every place to follow along, and anything that is for sale. The person on the other end walks away with something they can keep on their homescreen instead of something that ends up in a drawer.
MyCliq pages were designed to be viewed on a phone, so desktop needed an answer of its own rather than a stretched version of the mobile layout. The fallback presents the page in its native proportions alongside a QR code, letting anyone scan it and carry on browsing on their phone, exactly where the page was meant to be seen.
3D Commercial
Branding
The Landing Page
The landing page was built to be read easily and to carry a prospective customer through a journey: what MyCliq is, how it works, and what it can do for them. Rather than explaining that in paragraphs, I created individual 2D animations for each idea, so the mechanics land in a glance instead of a page of text.
It also ships with an interactive page builder embedded directly in the site. Visitors can start customizing before they sign up for anything, which turns an abstract promise into something they have already felt for themselves.
Technical Breakdown
Overview
MyCliq is a link-in-bio platform, but the product decision that shapes everything underneath it is that a link is not just a URL. It is a container. Any link on a page can expand into a full subpage: a long-form post, a blog grid, an image gallery, a video or music embed, a product listing, or a paid service page that takes money through the creator's own payment account.
In production that adds up to roughly 286 source files, 43 API route handlers, around 30 database migrations, a page builder with live preview, two payment systems, three storage backends, and a public render path that has to stay fast for visitors who will never sign up for anything.
The stack: Next.js, Tailwind CSS, Node.js, and PostgreSQL, with JWTs for authentication, Stripe for payment processing, a REST API for external integrations, and PWA support so the product installs to a phone homescreen.
Two Workloads
The hard part is that one codebase serves two workloads with almost opposite requirements. The builder is authenticated, low volume, and structurally complex: every field is writable, and the latency budget is generous because it is a tool someone sits inside for an hour. The live page is the opposite. Unbounded anonymous traffic, a simple read-only render, and a brutal latency budget, because it is a link someone tapped from a story.
Serving both through one data path gives you either a slow builder or an expensive public page. Most of the architecture below exists to keep those two paths separate without maintaining two copies of the domain model.
01
Next.js, App Router, Server Components
Public pages render on the server, so a visitor downloads markup instead of an application.
02
Postgres with Row Level Security
One managed service for data, identity, and authorization enforced at the database rather than only in app code.
03
Redis, serverless native
A cache spoken to over HTTP, shared across every region, that survives deploys instead of warming up after each one.
04
Storage that is not billed per download
Every visit to a creator page pulls their images out of storage. Most providers charge for that outbound traffic, so a page going viral raises the bill. This one charges for what is stored, not for how often it is served.
05
Two separate payment systems
Platform subscriptions on one side, creator payouts on the other, kept apart by construction.
06
Off the shelf product analytics
Replaced a homegrown events pipeline that was competing with the live page for database time.
Request Path
When a visitor opens a creator's URL, authentication middleware is skipped entirely, because a public page has nobody to authenticate. Checking a session is a network call per request, and running it on the one route that needs to be fastest was adding a round trip for no reason. A thin lookup resolves the username to that creator's active page, and the render begins.
Before touching the database, the renderer reads a cache configuration scoped to that specific page: how long to cache, which strategy to use, and whether anything should bypass it. On a stale-while-revalidate strategy the cached copy returns immediately and a refresh fires in the background, so no visitor ever waits for the refresh that benefits the next one. On a miss, Postgres serves the render and the cache is warmed afterward, outside the request.
Three cache layers stack deliberately. Redis is shared across every region and survives deploys. The CDN is driven by cache headers the page computes per request, so the edge and Redis agree on lifetime instead of fighting over it. And a request-level memo dedupes the page fetch between generating social preview tags and rendering the body, so building a link preview does not cost a second database read.
Caching
The first version cached the entire hydrated page. It was the obvious implementation and it was wrong in two directions. Payloads ballooned with nested subpage content and long asset URLs, which costs real money on a plan billed per command and per byte. Worse, editing any single link invalidated the whole blob, so hit rate collapsed for exactly the most active creators, who generate the most traffic.
The fix was to split the page. Identity and presentation, meaning theme, bio, profile picture, and settings, stay cached because they rarely change. The heavy, frequently edited data, meaning links, widgets, and gallery images, is fetched separately and merged at render time. A theme tweak and a link reorder now invalidate different things, and the cheap, hot part of the page stays warm. The cost is more merge code and more places to miss a null check, paid for with graceful degradation: a missing widget renders nothing rather than a server error.
Cache configuration lives in the database, per page, rather than in code. Caching bugs on a public page are the worst kind, invisible to the person who can fix them and permanent-looking to the person reporting them. Being able to disable caching for one page, or add a bypass for one creator, without shipping a deploy turns a class of incident into a support action.
Every cache path fails open. No cache configured goes straight to the database. A failed read counts as a miss. A broken bypass rule bypasses the cache. The entire caching layer can be down and the site still serves correct pages, only slower.
Payments
There are two payment integrations and they are never allowed to touch. One handles platform subscriptions across the free, premium, and ultimate tiers. The other lets creators connect their own account and sell service pages directly, where the buyer pays the creator and MyCliq never holds the funds.
They live in separate server-only modules, and that separation is structural rather than a convention someone has to remember. The payout logic cannot be accidentally imported into a client component without the build failing, which is the only reliable way to keep a server secret out of a browser bundle.
Webhooks verify every payload signature before a single byte is parsed. One subtlety drove real design: a user who picks a paid plan during signup has no profile and no page yet, only a checkout session. The account is created by the webhook once payment actually succeeds, with the signup details carried through as checkout metadata. That avoids the familiar bug where an abandoned checkout leaves a half-created account squatting on a username.
Uploads
Image uploads never pass through the application server. The server issues a presigned URL, the browser uploads directly to object storage, and a second call registers the metadata. Streaming user files through a serverless function burns execution time and memory on work that adds nothing. The server does the two things it is actually needed for: validating the request and recording the result.
Files are compressed and converted to WebP in the browser before upload, so plan quotas measure what is actually stored rather than whatever someone happened to drag in.
Authorization
Plan limits are enforced in two independent places. The interface hides what a tier cannot use, and the server re-derives the plan and applies its own limits regardless of what the interface did. Client-side gating is user experience. Server-side gating is the actual rule.
Authorization itself lives in the database through row level security rather than only in application code, and that policy earned its keep. A security audit surfaced privileged database functions that trusted a caller-supplied user id. Because page owner ids are publicly readable on active pages, any signed-in user could have deactivated or deleted any live page on the platform. The fix asserts the caller's real identity inside the function itself, and the same pass tightened execute permissions and pinned search paths across every remaining privileged function.
Public API routes are rate limited by IP at the edge in two tiers. Routes that make outbound requests on the platform's behalf, like image proxying and third-party product lookups, are limited far more tightly than ordinary reads, because those are the ones where an attacker can make the platform pay for their traffic.
Reliability
Critical writes retry with capped exponential backoff, deliberately gentler than doubling, to avoid stampeding a database that is already struggling. More important than the backoff is the distinction it draws: network failures retry, business logic failures do not. A username that is already taken throws immediately. Blindly retrying a non-idempotent write is how one failed signup becomes three accounts.
The least glamorous code in the system is among the most valuable. Certain antivirus products intercept encrypted traffic to the database provider for a real subset of users, and the symptom is simply that the site appears broken, with no actionable signal for anyone. The retry layer recognizes those specific failures and returns step-by-step instructions for allowlisting the domain, which converts an unsupportable bug report into a self-service fix.
Trade-offs
Honest accounting is more useful than a clean story. The caching strategy was the second design rather than the first, and the question that would have produced it, what invalidates this, was available before any code was written. A full analytics system with an events table, materialized views, and a dashboard was built and then deleted in favor of an off-the-shelf product that did it better. That is weeks of infrastructure spent learning a narrow and reusable lesson: build the part that is differentiated, buy the part that is not.
The database access layer has grown past a thousand lines and mixes concerns that should be separate modules. It has not caused a bug. It is simply the file nobody wants to open, which is its own kind of debt.
Takeaway
The single decision with the largest payoff was treating the public render and the authenticated builder as genuinely different systems that happen to share a domain model, with different caching, different authentication handling, and different failure behavior. Nearly every performance and cost improvement traced back to that split.
The second was deciding once where each kind of failure should land, and then staying consistent. Caching fails open. Rate limiting fails open. Payment webhooks fail closed, with signature verification before anything else happens. Business logic errors never retry. None of that is arbitrary. It follows from what a wrong answer costs in each place.