DEV Community

Cover image for I built a free concert tracker solo: 13 languages, 11,000 prerendered pages, one React codebase for iOS, Android and web
Alexandra Pytel
Alexandra Pytel

Posted on

I built a free concert tracker solo: 13 languages, 11,000 prerendered pages, one React codebase for iOS, Android and web

I go to a lot of concerts. In January 2025 I got tired of a spreadsheet that could not tell me which opener I had seen twice, so I started building Gigvault, a concert diary that logs the shows you actually went to. Twenty months later it runs on iOS, Android and the web in 13 languages, and logging concerts is still free. This is the technical side of that: what worked, what I would not do again, and the one decision I keep having to defend.

The product in one paragraph

You log a show. Gigvault finds the artist, the venue, the date and the full lineup, so a three-day festival becomes fifteen logged sets, not one row. After the show, every band's setlist appears on its own (the setlist database behind it holds 9.9M+ setlists). A "Concert Wrapped" is rebuilt after every gig instead of once in December: shows, unique bands, cities, minutes live, most-seen artist, top venue. Photos, videos, notes and a rating hang on the night they belong to, private by default. "Concert Buddies" shows which friends were at the same shows across your whole history; connections are mutual-only, there is no follower graph and no algorithmic feed.

Stack

React + Vite + TypeScript on the front, Supabase (Postgres, Auth, Storage, Edge Functions) on the back, Capacitor for the iOS and Android builds, Cloudflare in front of the web app. One codebase, three targets. Nothing exotic, and that was the point: as a solo dev I cannot afford a stack where the interesting part is the stack.

Three things that turned out to be the real work

1. Lineups, not headliners. Most concert apps store one artist per event. That makes stats wrong for anyone who cares about openers, and it makes festivals useless. Gigvault stores every act as its own logged set with the event as the parent. Everything downstream (stats, Wrapped, buddies, badges) queries sets, not events. It doubled the data model complexity early on, and it is the single feature people mention most, so it was worth it. If you build anything in this space: model the set, not the show.

2. i18n at 4,722 keys. The UI ships in English, German, French, Dutch, Spanish, Portuguese, Italian, Polish, Czech, Danish, Norwegian, Swedish and Finnish. Every locale file is complete; a missing key in one language is a bug, not a fallback. Lessons: (a) pluralization and date formatting are where translations break, not vocabulary, so use Intl everywhere and never concatenate strings; (b) the marketing pages and blog are a separate, smaller language set (7) with their own route map, because a translated blog post that nobody reads is pure maintenance; (c) treat the locale JSON as code: reviewed, diffed, no "quick fix" in a spreadsheet.

3. Prerendering a SPA for SEO. A Vite SPA is invisible to search engines for anything but the shell. Gigvault's public entity pages (608 festivals, 713 cities, 7,426 artists, 2,173 venues at the last build) are prerendered into static HTML at build time, per language where a translation exists, and served as plain files. The homepage carries a prerendered "above the fold" block inside index.html so that crawlers and badge verifiers get real content before React hydrates. A small Cloudflare Worker canonicalizes entity URLs so that spelling variants of the same artist or venue do not become two pages. If you do this, watch your HTML size: my homepage is ~200 KB and at least one third-party crawler only reads the first ~100 KB, so anything it must find goes into the <head>, not the footer.

What I would not do again

Shipping the native apps before the web app had a public, crawlable surface. For the first months the only way to discover Gigvault was the App Store. The prerendered entity pages and the blog are what bring search traffic now, and they cost nothing per visitor. Search is the distribution channel a solo dev can actually afford; native stores are not.

The decision I keep defending: logging is free, unlimited, forever

The core of the product (log unlimited shows, full lineups, automatic setlists, the diary, Concert Wrapped, basic buddies) costs nothing, has no ads, and never asks for a card. The paid tier ($2.99/month) adds unlimited photo and video uploads, an AI-written "Music Identity" portrait, concert groups with shared planning, recommendations, advanced analytics with monthly Wrapped, and export/backup. A separate Photographer Pro plan ($4.99) exists for people who shoot shows.

The argument against this is obvious: the free tier is the whole reason to install the app. The argument for it is the one I find more convincing: a concert history is worth exactly nothing if it is incomplete, and a paywall on logging guarantees it will be. Everything paid is layered on top of a complete record, never in front of it. Whether that is a business remains to be seen; as a product it is the only version I would use myself.

Numbers, for the curious

13 UI languages, 7 content languages, ~11,000 prerendered pages, 82 countries with logged concerts, one developer. The web app needs no download: gigvault.app. Festival lineups for 2026 are browsable without an account at gigvault.app/festival-tracker.

If you are building on Supabase + Capacitor and hit the same walls (prerendering, i18n at scale, one data model for three targets), the comments are open. I will answer everything I can.

Top comments (0)