Repository
FreelanceStack
A review site that scores thirty pieces of freelancer software and compares them head to head, built to be found by people searching for exactly that comparison.
Thirty tools, five weighted dimensions, one hundred and seventy-eight pages.
What it is
An independent review site for freelancer software: thirty tools scored across five weighted dimensions, rendered as a homepage, thirty reviews, thirty discount pages, eleven categories, and sixty-two head-to-head comparisons. The comparison page is the unit of value — somebody searching for one tool against another has already decided to buy and is choosing.
Why it exists
A distribution experiment, not a software problem. Affiliate review sites work when they rank, and ranking for long-tail comparison queries is a content-generation problem at a scale nobody wants to write by hand: thirty tools produce hundreds of plausible pairings. The machine was built once, proved in one market, and pointed at a second — the rubric, the comparison generator and the prerender pipeline are market-independent, and only the dataset changes.
As a business
The only thing here where revenue was the explicit goal. Affiliate commission earned by ranking for purchase-intent queries, with near-zero cost because the output is static — content cost once, traffic compounds or it does not. The honest caveat is that it is a thin-content risk by design: search engines penalise programmatic affiliate comparisons, and the line between a useful comparison site and a doorway page is drawn by somebody else's algorithm.
Stack
- Client
- React · Vite · Tailwind
- Data
- Supabase
- Build
- Custom prerender pipeline
The decision
The problem
Programmatic SEO fails in a specific way: sixty-two comparison pages assembled from a template are sixty-two near-duplicate pages, which rank for nothing and can penalise the whole domain. The pages have to differ substantively, not just in the two names in the headline.
What I did
Score first, generate second. Because every tool carries a five-dimension weighted score, a comparison is derived rather than templated — the dimensions where two tools actually diverge are computed, and those differences are what the page is about. Two tools that score alike on value but differ on depth produce a different page from two that differ on price.
What it costs
Entirely dependent on the scores being real. If the ratings are casually assigned, every derived page inherits that arbitrariness and the whole site becomes the thin content it was trying not to be. The rubric is the load-bearing part, and it is human-authored.
Where it ended
Shipped and live in a single commit, and not touched since. Whether that is finished or abandoned depends on traffic data.
A piece of the code
src/data/tools.ts
const SCORE_WEIGHTS = { value_for_money: 0.35, ease_of_use: 0.25, feature_depth: 0.18, integrations: 0.12, customer_support: 0.1,} as const;/** * Convert the five 0–100 dimension scores into a single 0–5 overall, weighted * to match what freelancers actually care about (value, then ease). */export function computeOverall(t: Pick<Tool, keyof typeof SCORE_WEIGHTS>): number { const weighted = t.value_for_money * SCORE_WEIGHTS.value_for_money + t.ease_of_use * SCORE_WEIGHTS.ease_of_use + t.feature_depth * SCORE_WEIGHTS.feature_depth + t.integrations * SCORE_WEIGHTS.integrations + t.customer_support * SCORE_WEIGHTS.customer_support; return Math.round((weighted / 20) * 10) / 10; // 0–100 → 0–5, one decimal}Excerpt — trimmed for reading, not a full file.