Skip to content
New: Customize your theme with an AI assistant. Read the guide

Service Areas

Files: src/content/areas/english/ (one file per area, plus -index.md), src/content/areas/french/ · Skill level: Beginner

A Homio area page

A service area is a town, district, or group of postal codes your crews cover. Each area is one file in src/content/areas/english/ and one page at /areas/<file-name>/. From those files Homio builds:

  • the areas page /areas/, with an area finder (type a ZIP code or a town),
  • one page per area, with its local phone, hours, neighbourhoods, prices, reviews, and a recent job,
  • the Areas menu in the header and the Areas column in the footer,
  • the ZIP check on the booking form,
  • areaServed in the structured data.

The four shipped areas (North Harbor, Elm Park, Westfield Heights, Old Mill) are fictional, with sample ZIP codes. Replace them with your real areas before you launch.

An Area File

From src/content/areas/english/elm-park.md:

title: "Elm Park" # area name: lists, menus, breadcrumbs, schema `name`
heading: "" # optional H1; empty uses "Home services in {area}" (i18n `areas.heading`)
description: "Older family houses under big trees, three ZIP codes, and the streets where our first van parked." # lists + meta description (≤ 155 characters)
metaTitle: "Cleaning and handyman services in Elm Park"
region: "East side" # sub-line in area lists
code: "02" # ticket number ("AREA Nº 02"); empty uses `order`
order: 2 # position everywhere (lowest first)
image: "/images/areas/elm-park.jpg" # 16:9 street or houses, no people. Optional
map: "" # optional static map image of YOUR real area; the samples are fictional
zips: ["55504", "55505", "55507"] # sample; the area finder and the booking ZIP check match these
neighborhoods: # sample; the finder matches them too (case and accents ignored)
- "Elm Park Village"
- "Linden Row"
aliases: ["Elmpark"] # other spellings or nearby towns the finder matches
# Local contact. Each field falls back to [business] in config.toml.
phone: "+15550100162" # sample (555-01xx fiction range), E.164 for tel links and schema
phoneDisplay: "(555) 010-0162" # sample
hours: {} # same per-day format as [business.hours]: { mon: "07:30-18:00", …, sun: "" }. Empty uses the business hours
office: {} # ONLY a real, staffed location (street, locality, region, postalCode). Never invent one
crews: 3 # sample: crews based in this area
since: 2014 # sample: first year served
services: [] # service ids offered here; empty = every service
serviceNotes:
- service: "gutter-cleaning"
note: "Busy late Oct to Nov: book two weeks ahead"
introTitle: "Working in Elm Park"
notes: # 3–4 local facts; `label` is a short mono tag
- label: "Parking"
title: "Permit zone on weekdays"
text: "Parts of Linden Row are permit-only from 8:00 to 18:00."
faq:
- question: "Do you work in the Fernwood side streets?"
answer: "Yes, every street in 55507, including the private lanes off Cedar Commons."
nearby: ["north-harbor", "old-mill"] # area ids for "Also nearby"
draft: false
---
Elm Park is where the company started: one van, a written checklist, and the houses around
Schoolhouse Green. …
FieldWhat it does
titleThe area name everywhere: menus, lists, breadcrumbs, structured data
headingThe page title. Empty gives “Home services in Elm Park”
descriptionThe line under the title and the meta description. Keep it under 155 characters
region, code, orderSub-line in lists, the number on the area ticket, and the position (lowest first)
image, mapA 16:9 photo of the area, and an optional static map of your real area
zipsThe postal codes this area covers. The finder and the booking form match them exactly
neighborhoods, aliasesPlace names the finder also matches. They are listed on the page under “Neighbourhoods we cover”
phone, phoneDisplay, hoursA local phone line and hours. Each empty field uses [business] in src/config/config.toml
officeOnly for a real, staffed office in this area. It adds the address to the page and the structured data
crews, sinceFacts on the area ticket (“Crews based here”, “Serving Elm Park since 2014”)
services, serviceNotesThe services offered here (empty means all) and a short note per service
introTitle, the Markdown bodyThe intro section (“Working in Elm Park”)
notes3 or 4 local facts, each with a short label
faqQuestions for this area
nearbyArea ids shown under “Also nearby”

On an area page, the header phone, the phone bar on mobile, and the area ticket use the area’s own phone when it is set. The ticket’s price line (“Standard clean from $110”) is the “from” price of the first rate card the area offers (see Pricing & The Estimator).

The page also shows the services offered there, reviews whose area is this area, and the newest project whose area is this area (hidden when there is none). To change those sections on one area, add a named block to its file, for example reviewsSection: { limit: 5 }.

The Areas Page

/areas/ is set by src/content/areas/english/-index.md. Its page header counts your areas, neighbourhoods, and ZIP codes (summary: true), and the area list comes from the area files:

areasSection: # defaults: areas-section.md
finder: true # ZIP code or town lookup above the rows
options:
layout: "list" # default (finder + area grid) | list (finder strip + one directory row per area)

The same section can appear on other pages with four layouts: default (area pills and the finder beside a photo), list (one row per area), finder (intro beside the finder panel), and tabs (one tab per area with hours, address, phone, and email). /home-cleaning/ uses it. Its defaults are in src/content/sections/english/areas-section.md; areas: [] shows every area, or list ids to show some.

The Area Finder

The finder is the box where a visitor types a ZIP code or a place name. It matches:

  • An exact ZIP code from zips: it goes straight to that area.
  • The start of a ZIP code (3 digits or more): it suggests the areas with matching ZIP codes.
  • A name: the area title, its neighborhoods, and its aliases. Case, accents, hyphens, and apostrophes are ignored, so “saint eloi” finds “Saint-Éloi”. Names that start with the text come first.

It shows up to 6 suggestions. Every area is built into the page, so with more than 150 areas the build prints an [areaFinder] warning; use the list layout then.

You don’t add areas to the menus by hand. In src/config/menu.en.json (and menu.fr.json), the header item with "source": "areas" lists the published areas, and the footer column with "source": "areas" lists them before its own “All areas” link:

{
"enable": true,
"name": "Areas",
"url": "/areas/",
"hasChildren": true,
"source": "areas",
"limit": 8
}

limit is the number of areas in the header menu (8 by default); the menu always ends with an “All areas” link. Above 6 areas the list splits into two columns. See Menus.

Areas In Structured Data

Homio writes areaServed from your published area files only. It never invents a place.

  • Every page: the business lists each area as an AdministrativeArea with its name and page URL.
  • Service pages: the service lists the areas that offer it (areas whose services is empty or includes it).
  • Area pages: a service named after your business and the area (“Your Company in Elm Park”) with the area’s region and postal codes, and the services offered there. With a real office, the page instead describes a branch of the business with that address, phone, and hours.

With no area files, areaServed is left out. See SEO & AI Visibility.

Add An Area

  1. Copy an area file in src/content/areas/english/, for example elm-park.md, and rename it. The file name is the URL: riverside.md is /areas/riverside/.
  2. Change title, description, region, code, order, and the photo.
  3. Set zips to the real postal codes, written exactly as people type them. Add neighborhoods and aliases.
  4. Set or clear the local phone, hours, crews, and since. Keep office: {} unless you have a real office there.
  5. Write the notes, faq, and the Markdown body about the area. Set nearby to the ids of neighbouring areas.
  6. Copy the file to src/content/areas/french/ with the same file name and translate the text. Keep zips, ids, and numbers the same.
  7. Run npm run dev, open /areas/, and type one of the new ZIP codes in the finder.

The header menu, the footer column, the finder, the booking ZIP check, and the structured data pick up the new area on their own.

Remove An Area

Delete the file in both english/ and french/, or set draft: true in both to hide it. Then search your content for its id and update:

  • nearby in the other area files,
  • area: in reviews (src/content/reviews/) and projects (src/content/projects/). A review whose area id no longer exists shows the id as plain text.

Also check the copy that names your areas: the metaDescription and the questions in src/content/areas/english/-index.md, and the description in src/content/sections/english/areas-section.md (“Four areas, each with its own crew base”).

Do It With AI

Replace the sample service areas with mine: [AREA NAME: ZIP CODES, NEIGHBOURHOODS, LOCAL PHONE
OR "use the main number"; ...]. Use src/content/areas/english/elm-park.md as the model, one file
per area named after the area, in order [ORDER]. Keep office: {} empty. Delete the sample areas
I don't use, and fix every `nearby`, review `area:`, and project `area:` that pointed at them.
Do the French files with the same file names, then update the area counts in the copy of
src/content/areas/english/-index.md and src/content/sections/english/areas-section.md.
Type something to search...