Artisan Plombier Jabre : Local SEO website for a plumber in Oise
About this project
The goal was straightforward: turn every local search like "plumber Beauvais" or "water leak Compiègne" into a phone call or a quote request. I built a marketing site with Next.js 16 and the App Router, using a data-driven approach where each city and each service has its own page with unique content, a dedicated JSON-LD schema and a targeted FAQ. Everything lives in typed TypeScript data files, so adding a city or a customer review never requires touching page code. On the conversion side, every phone CTA is tracked through PostHog in cookieless mode to measure what actually converts, and a sticky call button on mobile captures urgent traffic. Before/after project shots are showcased through an interactive slider and a fullscreen video player that immediately build trust. Open Graph images are generated per route with Satori, so every page shared on WhatsApp or social platforms gets its own branded visual without a single image asset to maintain.
Key Features
- Data-driven architecture with 7 cities and 4 services: Each city and service page pulls unique content from a typed TypeScript data file, so adding or editing a coverage area never requires touching template code.
- Full local SEO with JSON-LD schemas: LocalBusiness, CityBusiness, Service, FAQ, Breadcrumb and AggregateRating schemas generated dynamically from data to maximize Google rich snippets.
- Cookieless GDPR-compliant conversion tracking: PostHog running in memory mode on the EU endpoint, with 6 dedicated business events to measure calls, quotes and emergency clicks without a cookie banner.
- Interactive before/after portfolio: CSS clipPath slider for photos and a fullscreen video player via React Portal to showcase bathroom renovations and water heater installations.
- Dynamic Open Graph images: One unique OG image per route generated on the fly with Satori, for consistent sharing on WhatsApp and social platforms with zero image assets to maintain.
- Sticky mobile call button: Phone CTA pinned at the bottom of the mobile viewport to capture urgent traffic in a single tap, tracked separately from quote CTAs.
Main hero with promise and dual CTA
Main hero clear promise, trust badges and dual call/quote CTA
Services carousel
Carousel of the 4 main services with clear navigation
Before/after project portfolio
Project portfolio with category filter and interactive before/after slider
4-step process timeline
4-step process timeline that reassures visitors before they reach out
Covered cities grid
Grid of covered cities on the homepage with internal linking
Customer reviews with structured data
Customer reviews with AggregateRating structured data for Google rich snippets
SEO-optimized accordion FAQ
Accordion FAQ with JSON-LD targeting Google Position Zero
Final CTA banner and footer
Final urgent/quote CTA banner and footer with legal info and SIRET number
Bathroom renovation service page
Bathroom renovation service page with hero, commitments and long-form SEO content
Coverage area hub page
Coverage area hub page with per-city response times and neighboring towns
Contact page and quote form
Contact page with a react-hook-form form, business details and coverage area
Challenges
The real challenge on this kind of site isn't technical it's avoiding duplicate content across the 7 city pages, which tanks local SEO within weeks. I wrote unique copy for each town with its own intervention times and neighboring villages instead of duplicating a template. Internal linking also had to connect every service to the right cities and back, without creating a structure Google couldn't crawl cleanly. Finally, tracking had to stay GDPR-compliant without cookies while still capturing call and form conversions accurately solved with PostHog in memory persistence mode on the EU endpoint.
Learnings
On an SEO-driven marketing site, 80% of the value comes from quality data-driven content and rigorous JSON-LD, not from technical complexity. Splitting data from templates also made the site easy for the client to evolve: adding a review or a new city takes a few lines in a TypeScript file. On the conversion side, the sticky mobile call button combined with distinct tracking for blue CTAs (quote) versus red CTAs (urgent call) gave the client concrete data to refine messaging.