← Field Notes

Case Notes · 10 May 2026

How we built True Grit's location-page system

True Grit Cleaning Co is a small, owner-run cleaning operation covering Richmond, Catterick Garrison, Darlington, and the Yorkshire Dales. When we built their site at the start of 2026, the brief had a specific local-SEO requirement: own the local search results for “cleaner in [village]” across every village in their catchment.

There were about eighteen villages in scope at launch. There would be more later — the business was actively growing into adjacent catchments.

The naive way to handle this is to write eighteen location pages by hand. The slightly less naive way is to write one template and stamp it eighteen times with the town name swapped in. Both of those produce thin, duplicated content that Google has been actively suppressing since 2023.

This is what we did instead.

The architecture: one data file, generated pages

Every village in the service area lives as a single entry in a TypeScript data file. The entry looks roughly like this:

  • Slug for the URL (/darlington, /catterick-garrison, /leyburn, etc.)
  • Display name, county, postcode
  • Latitude and longitude
  • Intro paragraph — written specifically for that village, not templated
  • A localContext block describing the catchment in 2–3 sentences
  • An array of typical client jobs in that location (holiday-let-heavy, military families, agricultural)
  • An array of named neighbouring villages within the service area
  • A localFaqs array — 2–3 questions genuinely specific to that town’s typical jobs

The page template then composes those fields into a real, useful page — with a header section, a local context block, a service list, a town-specific FAQ block, and a “nearby villages we also serve” cross-link cluster. Schema is generated per location automatically: LocalBusiness, Service, FAQPage, BreadcrumbList, all tied together in a single @graph block so Google sees them as one entity.

The crucial bit: each page is actually substantive

The pattern that makes this work — and that most agencies skip — is that every entry has unique content. Not “we clean in [town], one of the lovely places in Yorkshire”. Real local detail: the postcode pockets, the kinds of properties common in that village, the typical job patterns, the named neighbouring areas. Two or three paragraphs that you could only have written about that specific place.

For True Grit specifically, this was easier than it sounds because the owner, Nikki, knows every village personally — she’s worked them all. We spent two days going through them with her, taking notes. The result was eighteen substantively different pages.

Each page ended up at around 700–900 words of unique content. That’s well clear of the doorway-page threshold (Google’s published guidance treats sub-200-word location pages as borderline). It also gave the buyer something useful to read.

What the schema does

Per page, the schema graph contains four connected nodes:

  1. LocalBusiness for True Grit itself, with the catchment town added to areaServed. NAP consistent across every page (this matters more than it sounds).
  2. Service for “Cleaning in [Town]”, tied to the LocalBusiness as provider.
  3. FAQPage with the town-specific FAQs.
  4. BreadcrumbList anchoring the page in the site’s information architecture.

All four nodes connect via @id references so Google sees them as one entity, not four detached blocks. That’s the difference between schema that “validates” and schema that actually moves the needle.

Adding a new town in five minutes

The pay-off is operational. When True Grit picks up a new catchment — say, they start cleaning regularly in Bedale — adding /bedale to the site is a five-minute job. Nikki gives us the intro and two FAQs over a quick call. We add one entry to the data file. The page builds itself with full content, full schema, and full internal linking into and out of neighbouring towns.

Within forty-eight hours, the new page is indexed and starting to rank for “cleaner Bedale”. Within a couple of weeks, it’s in the local pack. The site grows with the business at zero structural cost.

Why we don’t do this with WordPress

People ask. The honest answer: you can, but the architecture fights you. WordPress page-builders make per-location pages unwieldy at scale, custom post types help but require ongoing maintenance, and the resulting site is slower than a hand-built static one. We use Astro for builds like this because the data-driven model is a perfect fit and the output is static HTML that ranks brilliantly.

What this looks like for other businesses

You don’t have to be a cleaning company to use this pattern. Any business that serves multiple named locations — a law firm, an estate agent, a removals company, a trades business, a tutor with a service radius — can benefit from a properly built location-page system.

The non-negotiables are:

  • Each page has substantive, town-specific content (not templated)
  • Schema is generated per location and tied together as one entity
  • NAP is consistent everywhere
  • Internal linking reflects real geography (not arbitrary alphabetical lists)
  • Adding new locations is a five-minute operation, not a five-hour one

Get those right and the location-page architecture compounds. Each new town is another search foothold. Every existing town benefits from the strengthened topical authority. The site grows with the business — quietly, structurally, in the background.

True Grit launched with eighteen locations. They’ll be at thirty by next spring. The site is built for that.

(QUICK QUESTION)

Got a question after reading this?

One paragraph. Not a brief, not a meeting — just a question. We come back the same day where we can.

(07 — START SOMETHING) LAUNCH PRICING LIVE · 3 STARTER BUILDS AVAILABLE
LAUNCH OFFER

Websites from £250 and care plans from £19/month.

01 Open 02 Open 03 Open

START A PROJECT.

Tell me what kind of site you need. I come back within a working day with a clear price, scope, and next step — straight from me, the person who’ll be doing the work.