Shipping a novelty tool on Vercel
The stack, the build, the afternoon that stretched into a fortnight, and the parts that were easier than expected.
2026-03-27

Puritanizer was described internally as "an afternoon build" for so long that we almost believed it. Afternoon is generous. It took about a week of actual work across a fortnight of elapsed time, which is standard for an afternoon build.
Here is the stack, for anyone building something similar.
The stack
- Next.js 15 (App Router, React 19, Server Components by default)
- TypeScript everywhere — no loose files
- Tailwind CSS with a custom palette (parchment, ink, plum, sepia, linen)
- Framer Motion for the spring-physics interactions
- sharp for server-side image processing (watermarks, resizing, webp conversion)
- Postgres via a hosted provider for user accounts and credit balances
- JWT auth with httpOnly cookies (no third-party auth service)
- PayPal for checkout — no subscriptions, no card-on-file
- Gemini 2.5 Flash Image for the actual image editing
- Vercel for hosting, preview deploys, and letting us never think about infrastructure
That is the entire dependency list of any consequence. The node_modules tree is larger. It always is.
What was easy
Vercel preview deploys. Every push to a branch gets its own URL, every PR gets a comment with that URL, every deploy is free to roll back. This is the single best piece of tooling in the modern web stack and we are grateful every time we use it.
Next.js App Router server components. The bits of the app that do not need interactivity (pricing page, blog, terms, privacy) render on the server without a whisper of client JS. The pages are fast because they are mostly HTML.
sharp. Compositing a watermark onto a generated image is three lines of code. Converting PNG to webp is one line. Resizing for different breakpoints is one line. sharp has been quietly carrying image-heavy Node apps for years and we did not have to think about it once.
What was harder
The prompt engineering for Nano Banana. Getting the model to paint clothes instead of swapping faces took most of the first week. We wrote about this in Prompt engineering a nun.
Rate limiting the free tier. The naive version used a cookie and was trivially defeated by opening an incognito window. The current version uses IP-based limits on the server and a browser-local counter on the client, and it is slightly annoying to users behind strict corporate NAT who share an IP with dozens of colleagues. We have not fully solved this. It is a known issue.
Currency detection. We auto-detect the user's currency from their IP and show prices in GBP, USD, or EUR accordingly. This was three days of work for a feature most users will not consciously notice. It is worth it. A £1.99 price shown as £ to a UK user and $ to an American user is significantly less friction than asking everyone to mentally convert.
What we skipped
- A mobile app. The web works on mobile. We do not need to maintain two codebases.
- A waitlist or "coming soon" page. The tool is either shipped or it is not.
- User dashboards. The account page has credit balance and payment history, and that is all anyone needs.
- Analytics beyond basic usage counts. We have one number we care about (puritanizations per day) and we check it occasionally.
- A dark mode. The whole brand is parchment. Dark mode would be a different brand.
The result
Live, cheap to run, fun to work on, and the deploy pipeline is a single git push. Afternoon build, in the same way that "just a quick drink" is a quick drink.
Put some clothes on a photo.
Drop in any photo, pick one of 350 modest outfits, and Puritanizer paints it straight on. Face stays untouched. Two free a day.
Open Puritanizer