Riviera Locals
An idea that started as a UX bootcamp prototype years ago, finally turned into a real product.
View Live Site →
Where It Came From
Moving around means starting from zero each time. New city, new country: you need an accountant, a lawyer, maybe a doctor, someone to fix the washing machine. Finding them is harder than it sounds, especially when you don't speak the language well and don't know who to trust.
I've been through this more than once. The need for something like Riviera Locals never felt abstract. It felt personal.
The fun part: I'd actually designed this once before. Years earlier, during my UX bootcamp, one of my projects was Nuutown: a service directory and booking platform for expats to find what they need in a new city. The research was solid. The prototype looked good. I got good feedback. And then it sat there, because the gap between a prototype and a working product was still too large to close without engineering support.
Riviera Locals is Nuutown, finally built. Same problem, same target audience, years later, with tools that actually let me ship it.
The Build
The stack: Supabase for the backend, Astro for the frontend. I used Codex for the coding work this time, keeping Claude for writing and copywriting. Same general approach as the boulder gym directory, but with more confidence in the tools and more attention paid to design. Previous projects had a "functional first, pretty later" quality. This one I actually cared about how it looked from the start.
The MVP includes filters by city and category, individual business profile pages, and a guide section built to rank for local search terms. The kind of thing that would have taken months a few years ago.
Gathering the Data
The data question was where this got interesting. I'm already on the Riviera, which turned out to be a real advantage. I spent time in regional Facebook groups where expats ask for recommendations: who to call for tax advice, which English-speaking doctor people trust, which plumber actually shows up. I saved those posts, copy-pasted them into an LLM, and used it to extract the initial dataset. It's scrappy. It also works.
From there, the workflow is a two-step process. A crawling tool gathers listings and extracts reviews. Then an LLM reads the resulting CSVs and pulls out specific information: contact details, service coverage, languages spoken, signals about quality.
The filtering part took real thought. The goal is to avoid false positives. Language detection is a good example. The system analyses reviews to determine whether a business serves English-speaking clients. That sounds simple until you try to build it: a review that says "they don't speak English" is a negative signal, not a positive one. Getting that distinction right required careful prompt design and quite a bit of iteration. Negative signals that read superficially like positive ones will corrupt the data quietly, and you won't notice until someone emails to say the listing is wrong.
It was a lot of work to design an effective system. It was also genuinely fun. This kind of problem, deciding what counts, how to handle ambiguity, where to draw the line, sits right at the intersection of data design and product thinking. I enjoyed working through it.
Building the Data Structure
The data model question was harder than it looked. A directory needs to be searchable two ways at once: by location and by category. You want "accountants in Nice" and "all services in Antibes" to both work cleanly. The answer in Supabase was a relational structure: a services table, a categories table, and a locations table, linked through junction records. Once the model was right, filtering fell into place.
One thing I had to think carefully about was how different service categories relate to geography differently. A plumber needs to be in or near your city. A tax accountant who works remotely doesn't. Building a search that handles both correctly means the system has to understand category-level rules about regionality, not just filter by location field. Getting that logic right was a meaningful design decision.
There were also decisions about what data to keep, what to display, and how to handle the different profile tiers I plan to introduce. The basic listings are live. Richer profiles, with more detail and featured placement, come later. Getting the data model right now means those tiers can be added without restructuring everything.
One structural difference from the boulder gym directory: individual profile pages for each listing, not just a card in a filterable list. I'm curious to see how Google reacts to that. Each profile is indexable content about a specific business. Over time, that's a different SEO surface area than a single directory page with entries on it.
The Multi-Model Workflow
Like the EVSolarNC project, this one relied on more than one AI system. The split became fairly natural over time: Codex for the coding, Gemini for research and data gathering, Claude for writing and copywriting. Each does what it's actually good at.
When I got stuck on a design decision, I used them differently. Not as a tool to produce output, but as a thinking partner to reach a conclusion. Describing a problem clearly enough to ask a good question about it usually helped me think through it, regardless of what came back. Sometimes the most useful thing an AI does is ask you to be more specific.
Why This One
I didn't choose this niche because of high search volume. I chose it because I wanted to build something genuinely useful, for a community I'm part of, in a place I live. That's a different starting point than most directory projects.
Directory plays take time. You build the structure, add listings, wait for Google to catch up, refine the content. There's no shortcut to that. I'm fine with it. The site solves a real problem, and the data system is good enough that improving it over time is a process rather than a rebuild.
What's next: more listings, the guide section expanding, and at some point building out the business side. How local services find their way to the platform, and what value they get from being there, is the question I haven't fully answered yet.