The Project: Find Emergency Plumber
I recently launched Find Emergency Plumber, a curated directory of 24/7 emergency plumbing services. It sounds simple on the surface, but the architecture underneath is where the magic happens.
The numbers:
- **1,104 statically generated pages**
- **90 cities** with comprehensive listings
- **1,251 verified plumbers** with quality scores
- **Sanity CMS** for content and blog management
- **Dark theme** (Telegram-style design, because usability is SEO)
But this article isn't about the numbers. It's about what I learned trying to rank simultaneously in completely different markets with the same codebase.
Why Multi-Market is Hard (And Why It's Worth It)
Most directories you see online are optimized for a single country. It makes sense: different regulations, different local competition, different search behavior.
But here's the thing: if you build a robust directory in one market, the marginal cost of expanding to another is nearly zero. The code is the same. The infrastructure is the same. Only the data changes.
That means if you can rank in the United States, you can rank in the United Kingdom with almost the same effort. That's exactly what I tried to do.
The Tech Stack: Why Next.js 16 + Sanity + Supabase
This is where most developers get it wrong. They build directories with the wrong technology.
Next.js 16 (App Router) was my choice because:
1. Static Generation: I generate 1,104 pages at build time. Zero latency in production. Google sees pure HTML, not JavaScript rendering. That's pure SEO.
2. Incremental Static Regeneration: When a plumber updates their information in Sanity, the page regenerates automatically without a full rebuild.
3. TypeScript in strict mode: With 1,100+ pages, a single typing error could break hundreds of routes. Strict TypeScript saved me from disasters.
For the database, I chose Supabase (PostgreSQL):
- Stores 1,251 verified plumbers
- Fast queries to filter by city, availability, rating
- REST API is perfect for Next.js
For blog content (critical for SEO), Sanity CMS v3:
- Clean interface for writing plumbing emergency articles
- Portable Text for flexible content
- Native integration with Next.js via `next-sanity`
The Most Expensive Mistake: GSC Indexation
This is where I learned the hard way.
I launched the project with confidence. I had 1,104 perfectly optimized static pages. I waited for rankings. Nothing.
I check Google Search Console and discover:
- Duplicate canonical issues
- www vs non-www routes without clear redirects
- Vercel was blocking certain static assets
My commit from December 20 says it all: ``` fix: GSC indexation issues - www canonical, redirects, and static asset blocking ```
The lesson: Don't assume Google will index automatically. You need:
1. Consistent canonical tags: One version of each page 2. Clear 301 redirects: If you change URL structure, redirect 3. GSC verification: Submit sitemap, verify indexation 4. Don't block assets: Vercel optimizes images by default. Sometimes that breaks blog indexation
On December 18, I had another issue: ``` Fix 402 error: bypass Vercel image optimization for blog/Sanity images ```
Vercel tries to optimize images automatically. But if images come from Sanity, that process sometimes generates 402 errors. The solution: use `@sanity/image-url` to serve images directly from Sanity.
Multi-Region Strategy: How to Rank in UK and US Simultaneously
This is where architecture matters.
My URL structure looks like this: ``` /us/cities/new-york/ /us/cities/los-angeles/ /uk/cities/london/ /uk/cities/manchester/ ```
Each region has its own content tree. That means:
1. Google understands geolocation: `/uk/` pages rank in UK, `/us/` pages in the US 2. Regionalized content: I can have different information about local regulations 3. Same code, different data: A single Next.js component renders both regions
The magic is in how I generate routes. With `getStaticParams()` in Next.js, I generate:
- 45 cities in UK
- 90 cities in US
- Each city with its own page
- Each page with 1,251 plumbers filtered by location
That's scalability. I'm not writing 1,104 pages. I'm writing one template and letting Next.js replicate it.
The Blog: Why Content is SEO
I have 1,104 directory pages. But that's not enough to rank.
Google favors sites with E-E-A-T: Experience, Expertise, Authoritativeness, Trustworthiness.
A directory without content is just a list. A directory with a blog is an authority.
That's why I integrated Sanity CMS for the blog:
- Articles on "What to do in a water leak"
- Guides on "How to choose an emergency plumber"
- Regional service comparisons
Each article links to directory pages. That creates an internal relevance network Google loves.
Recent Commits: Building in Public
My latest changes show exactly where I'm focused:
December 27: ``` feat: add SEO improvements and emergency landing pages fix: use padding-inline for .container to fix Tailwind py-* utility conflicts ```
I added specific landing pages for emergencies. Because searching "plumber" is different from "emergency plumber at 3am". That intent is different.
I also fixed Tailwind conflicts. With 1,104 pages, a spacing error replicates everywhere. That's why I use TypeScript strict and Tailwind carefully.
December 25: ``` fix: add null-checking for mainImage.asset in blog components ```
This matters: when you use a CMS like Sanity, data sometimes arrives incomplete. An editor forgets to upload an image. Your page breaks. That's why you need null-checking in every component.
Lessons Learned
1. Static Generation > Dynamic Rendering for SEO
Google prefers static HTML. If you can pre-render, do it.
2. Check GSC Constantly
Don't wait 3 months to discover your pages aren't indexed. Check GSC every week.
3. Multi-Region Requires Clean Architecture
Don't try to "hack" multi-region with redirects. Design URLs from the start.
4. The CMS is Critical
Without Sanity, I couldn't update content without redeploying. The CMS is your scalability.
5. TypeScript Strict Saves Money
With 1,100+ pages, a typing error is a disaster. Strict TypeScript costs more to write, but saves infinitely on debugging.
What's Next?
Ranking is only the first step. Now comes:
1. Validate traffic: Is there real demand for this service? 2. Monetization: How do I convert traffic to revenue? 3. Scalability: Can I replicate this in other directories?
That's the next chapter.
Takeaway
Multi-market SEO isn't magic. It's clean architecture + patience + attention to detail.
It has three components:
1. Right tech stack: Next.js + Sanity + Supabase 2. Static generation: 1,104 pre-rendered pages 3. Obsessive monitoring: GSC, Search Console, indexation metrics
If you do that right, ranking in multiple markets is about replicating data, not rewriting code.
This is what building in public means: documenting not just the wins, but also the GSC errors, the Tailwind conflicts, the forgotten null-checks. That's what other developers need to know.
