Skip to content
AstroCraft Docs
On this theme

Services

Urbic ships four services — interior concept, space planning, 3D modelling and detailing — and they are the one part of the content layer that is not a collection. They live in src/config/servicesData.json.ts, and that is a deliberate holding position rather than an oversight.

Where they live and why

Each entry in servicesData is shaped as exactly what a src/data/services/<slug>/ frontmatter would carry: title, href, image, alt, summary, deliverables, and a body. The day you promote it to a collection, the route swaps its array walk for getCollection("services") and no section changes.

Three pages read the array today. The homepage carousel reads title, href, image and alt. /services/ additionally reads summary and deliverables per entry, plus the shared servicesProcess. And /services/<slug>/ is the only reader of body.

The four titles and hrefs are stated twice — here and in navData.json.ts, which declares them as the header’s Services flyout. That duplication is real and it is guarded: servicesData.test.ts fails the build if the two lists drift. They are restated rather than derived because a service entry needs a photograph the nav has no business carrying, and a real collection entry would own the whole thing as one frontmatter block anyway.

The routes

src/pages/services/index.astro is a thin shell composing the shared PageHero, ServiceRows, Process, Testimonials and Faq. One file — src/pages/services/[slug].astro — serves all four detail routes, deriving each param through serviceSlug from @js/services, the same function the index rows use for their anchor ids.

It is [slug] rather than [...slug] on purpose: a rest param would also match /services/a/b/c/ and would type slug as possibly-undefined, while getStaticPaths only ever emits one segment. /work/[slug].astro makes the same call for the same reason.

The service photos are stored as dynamic-import thunks so pnpm test can load the config under plain Node, and the route resolves them in parallel in getStaticPaths before handing sections the plain asset a collection would have given.

The process band

servicesProcess is five phases — survey, concept, drawings, and so on — rendered as ruled rows rather than five too-narrow columns. It appears on both /services/ and each service page.

It is worth knowing what it is not: contactData’s nextSteps is three rows describing what happens after you send the enquiry form (reply, visit, fee) and it stops where work begins. These five are the work itself. The two lists meet at the site visit and then diverge, which is why they are separate arrays rather than one shared list.

Promoting to a collection

If you want authors editing services from the CMS, promote the array:

  1. Define a services collection in src/content.config.ts with the fields ServiceEntry already names.
  2. Move each entry into src/data/services/<slug>/index.md, with the body becoming the entry body.
  3. Swap the array walk in src/pages/services/[slug].astro and index.astro for getCollection("services").
  4. Add a services block to collections in src/admin.config.ts, naming its title, description and social-image fields.
  5. Keep navData.json.ts in sync, or drop the nav parity check in servicesData.test.ts once the config array is gone.

Until then, editing a service is editing a typed config module — a normal file change, committed by you rather than by the CMS.

Replacing the sample copy

The five process steps and the four service descriptions are placeholder copy written in the voice of the invented studio. The photograph on /services/ is borrowed from the homepage’s concept section until the page gets one of its own; that is flagged in servicesData.json.ts itself.

NEXT STEPRouting