Skip to content
Twenty One Media
webSeptember 2, 2026

The Data Lives in site.ts Until It Shouldn't

Our site has a central config file, src/content/site.ts. It holds the content that drives multiple surfaces: the four /services/* local SEO pages, the about page, city-specific copy, FAQ blocks. Editing one object in one place propagates to every surface that reads it. That's the point.

But two recent changes made opposite decisions about whether something belongs there, and both were right.

The Array That Was There but Unused

The aboutPage.founder.photos object in site.ts listed five photos with their source paths and alt text. The about page didn't read from it. Instead, about/page.tsx generated paths with a [1, 2, 3, 4, 5].map() loop: isaac-${n}.jpg, pattern assumed, alt text hardcoded as "".

The array was the right abstraction. The page just stopped using it at some point and nobody noticed, because everything still worked.

When we rewrote the about strip for the new founder portraits, we reconnected them. The page now maps the array. Adding a photo, changing an order, or fixing alt text is a one-line edit in one file. The page has no opinion about how many photos there are or what they're named.

That's the case where site.ts is the right home: one canonical source, multiple places that render it, change-in-one-place semantics.

The Hub Page That Reads Nothing

When we rebuilt the /services hub page last month, we deliberately did not pull data from site.ts.

The services object in site.ts is structured for the city pages. It has localIntro fields for "Video production for Kokomo, Carmel, Fishers..." and faqs calibrated to local search queries. That data is doing local SEO work.

The hub page is doing something else: it's selling a single engagement. It has six numbered items explaining what's included and a fence listing the scope boundaries. That copy is calibrated for conversion, not for geographic keyword coverage.

If the hub read from the same services object, we'd have one data source serving two different jobs. Editing the hub would reach the city pages. Editing city copy would risk touching the sales page. The coupling would be invisible until someone made an edit they thought was local and found it showing up somewhere else.

So the hub page inlines its own data. No import from site.ts. The comment in the file explains it:

src/content/site.ts is deliberately NOT read here: its services object still feeds the four /services/* local-SEO pages and would drag them along with any edit.

The Rule

Centralize when the consumers need to move together. The about page photos and the about strip should always show the same photos. They should move together, so they share a source.

The hub page and the city pages are different pages with different jobs. They will evolve on different schedules, driven by different feedback. Forcing them to share a data source doesn't simplify anything. It just hides coupling that will surface at the worst time.

The same config file. Two entries. Two opposite decisions. Both intentional.