Residential exterior services contractorExterior services

A page for every service, in every town they serve.

Seven services across eight towns is fifty-six searches. Two lists and one template generate a page for each, and every one says something only that town could.

Published by Synetix Systems4 weeks engagement4 min read

The homepage: a headline about taking care of the work, the seven services named in a paragraph beneath it, and a photograph of a house being soft-washed.
The same page on a phone: headline, service list, and a full-width quote button above the fold.
Service and town pages, generated from two lists
56Service and town pages, generated from two lists
Pages prerendered to static HTML at build
76Pages prerendered to static HTML at build
Third-party scripts loaded before the page paints
0Third-party scripts loaded before the page paints

Anonymized write-up. The client is not named, and identifying details have been replaced in every screenshot. The figures are counts from the delivered build, not a company's audited business results.

Someone in El Dorado Hills wants their windows cleaned. They do not search for a company. They type "window cleaning el dorado hills" into a phone, and they pick from what comes back.

That search happens seven ways: windows, pressure washing, solar panels, gutters, moss, junk, permanent lighting, across eight towns. Fifty-six different searches, each one a person with a house and a credit card. A single page listing all seven services in a paragraph competes for none of them.

This is what we built for one exterior-services crew instead.

Why one page for seven services ranks for none of them

A services page that mentions window cleaning, pressure washing and five other things is not about any of them. It is a table of contents. Search engines treat it as one, and so do people: nothing on it answers the question that was actually typed.

The instinct is to fix that with more pages. That instinct is right and it is where most contractor sites go wrong, because the pages that get made this way are worse than having none.

  1. A page per town, made by find-and-replace. Same eight paragraphs, one word swapped. A reader notices in about four seconds, and so does Google.
  2. A page per service, with no town in it. Ranks nationally against companies who will never drive to your house.
  3. Fifty-six pages, hand-maintained. The price change lands on nine of them and the other forty-seven quietly disagree.

The third one is the real trap. It is not that the pages are hard to write. It is that they are impossible to keep true.

Two lists, one template, fifty-six pages

The site holds seven services and eight towns as data. Every combination of the two is a route, and the route builds itself.

  1. 01

    Two lists

    Services and towns are plain TypeScript. Each carries its own facts: what the job involves, what the town is near, which county it sits in, how far it is from the yard.

  2. 02

    One template

    The page assembles from both records. The service supplies the work; the town supplies the distance, the county, the neighboring towns and the local note.

  3. 03

    Everything follows

    Add a town to the list and eight pages, the navigation, the cross-links, the breadcrumbs and the sitemap all appear. Nothing is maintained twice.

Fifty-six of the site's seventy-six pages are generated this way. All seventy-six are prerendered to static HTML at build time, so what a visitor's phone receives is a finished document, not an application that has to boot before it can say what it sells.

A service-and-town page: the heading reads Window Cleaning in El Dorado Hills, CA, with a breadcrumb above it, local copy noting the distance from the company's headquarters, and a photograph of the work being done.
One of fifty-six. The town is in the heading, the breadcrumb, the distance from the yard, and the button. Client identifiers are replaced in every screenshot here.

What stops a generated page from being a thin one

The generator is the easy half. The half that decides whether the pages are worth having is what each record is required to carry.

FieldWhat it does on the page
Distance from the yard"About 15 minutes from our headquarters", a claim only this town's page can make
CountyPuts the page in the right administrative area for local search
Neighbouring townsCross-links that describe a real service radius, not a link farm
Per-service FAQSeven separate question sets, answered once and reused across all eight towns

None of that is decoration. Each field is the reason a given page is allowed to exist. A town we could not say anything specific about would not get pages, and that is a better outcome than fifty-six near-identical ones.

The form is the only thing on the site that matters

Every page exists to produce one action: a quote request. The client already ran their scheduling in Jobber, so the quote form is Jobber's own embed, which meant inheriting a third-party script on a site whose whole argument is that it loads fast.

Three things made that survivable.

  • It loads after the page, never before it. The embed is injected on idle. The page has painted and is readable before the script is requested.
  • Its connections are opened early. The two origins the embed reaches for are preconnected as soon as the form's page mounts, so the handshakes are already done when the deferred script finally fires.
  • It cannot move the page. The embed's loading spinner is absolutely positioned; given the chance it spans the viewport and the layout jumps when the real form replaces it. It is given a positioned container with a reserved minimum height, so the form arrives inside a box that was already there.

The third one is the one people skip. A form that shoves the page down as it loads costs you the visitor who was mid-sentence, and it costs you the Core Web Vitals score, and the second thing is how the first one stops finding you.

Google reviews get similar treatment: they are fetched from the Places API at build time and written into the source as data, so the live page carries the reviews without carrying Google's script.

When a matrix is worth building

Not every business needs fifty-six pages. This one is worth doing when three things are true at once:

  • The services are genuinely different jobs. Seven ways of cleaning a house is a matrix. Seven names for the same visit is padding.
  • The towns are genuinely different markets. Different counties, different drive times, different housing stock. If the only difference is the name, one page covering the region is more honest and will do better.
  • Somebody can say something specific about each cell. This is the whole test. If the local note has to be invented, the page should not be generated.

Get those right and the matrix is not an SEO trick. It is just the site saying, accurately, that this crew does this job in this town.

Have a workflow that looks like this?

Tell us where the time goes. We will tell you honestly whether it is worth automating, and roughly what it would take.

Start a conversation