Here’s a “component-candidate audit” of your config: places where the same field shape repeats (or would repeat with a tiny tweak), so extracting a component would meaningfully simplify the file.
I’ll group them by high-value vs nice-to-have, and I’ll note the small change needed (if any) to make them reusable.
Where: projects.slug, team.slug, experiments.slug, awards.slug, featured-clients.slug
Pattern is identical everywhere.
Component: slug_kebab (string + required + pattern)
Small change needed: none.
Where: projects.order, team.order, experiments.order, awards.order, featured-clients.order
Same semantics: “Grid order (lower = earlier)”, default 100.
Small change needed: none (except you might want consistent label everywhere).
Where:
projects.featured (default true, label “Pin to featured”)
experiments.featured (default true)
awards.featured (default true)
featured-clients.include (default true, label differs)
You have two “kinds”:
- Featured booleans
- Include/Show booleans
Component options:
featured_toggle (boolean default true)
include_toggle (boolean default true)
Small change needed: unify labels or accept that label differs (Pages CMS usually lets you override label even when using a component, but if it doesn’t, make two components).
Where:
client.url, collabs.url, exhibitions.url
team.links.url (within object)
experiments.url, awards.url, featured-clients.url
- plus
projects.external_url
These are all type: string and represent a URL.
Component: url (string) and optionally url_optional vs url_required
Small change needed: decide if you want a single URL validator (regex) everywhere. Right now only videos has a pattern.
Where:
team.links list items: { name, url }
site.navigation list items are similar-ish but not identical
- you already have
components.social_link which is basically the same structure
Component: rename/repurpose social_link to link_item and use it everywhere you have {name, url}
Small change needed: either:
- broaden
social_link to generic naming (Label, URL), or
- keep
social_link and create link_item alias.
Where: projects.seo uses component: seo
But site.seo is currently inline with type: object.
Component: reuse seo for site-level defaults too.
Small change needed: In site.fields, switch:
…and if you want slightly different labels (“Default SEO”), you can keep the label at the usage site.
You have two blocks:
Client block:
client_info (select via fetch)
client_role (select with fixed values + long description)
url
Collab block:
collaborator_info (select via fetch)
role (string)
url
You can componentize:
notion_select_client and notion_select_collab (since only URL differs)
role_string (string role label)
role_select_project_originator (your “Client / Commissioned By …” select)
Small change needed: accept that you’ll have two fetch-select components (no parameters), but you can still eliminate repetition in the field definitions.
Same as #1, but you may want:
slug_required
slug_optional
slug_project (if you later add a different message or allow underscores)
Small change needed: none; just anticipating drift.
projects.exhibitions list items:
name (required string)
year (required number)
url (string)
This is a clean component: named_year_link.
Small change needed: none.
Several collections share the pattern “name + slug + logo/thumbnail + url + order + featured/include”.
Examples:
- experiments:
name, slug, url, thumbnail, featured, order
- awards:
name, slug, url, logo, featured, order (+ extra metadata)
- featured-clients:
name, slug, logo, include, url, order
You can’t “spread” multiple components at once, but you can componentize sub-parts:
name_required
image_logo_optional / image_logo_required
image_thumbnail_required
Small change needed: mostly consistent labels (“Logo” vs “Grid thumbnail” etc.). If labels differ, create two components (logo_image, thumbnail_image).
site.navigation list items are:
label, slug, section, href
This is reusable if you ever add secondary navs / footer nav.
Component: nav_item
Small change needed: none.
site.socials is a fixed object with known platforms.
If you ever want to support arbitrary platforms, you could switch it to a list of link_item objects (name/url). That’s a schema change, but it simplifies editing and future expansion.
Small change needed: convert socials from fixed fields → list: true of link_item. (Only do this if your site code can handle it.)
Right now videos uses a broad URL regex, but most URL fields are unvalidated. You could:
- create
url_validated component
- apply to all URL fields
Small change needed: adopt one regex for all, or make url_any + youtube_url components.
These aren’t good component candidates unless Pages CMS supports more advanced composition (usually it doesn’t):
- Whole collection schemas (e.g. “common fields” for every collection) can’t be easily “mixed in” unless Pages CMS supports some kind of
extends / include concept. With plain components, you still have to list each field, but each field can reference a component.
- Fetch select fields cannot be parameterized; you’ll need separate components per endpoint.
If you do only 6–8 components, do these:
slug_kebab
order_default_100
featured_toggle
include_toggle
url_optional (and optionally url_required)
- reuse
seo for site.seo
link_item (replace/rename your social_link)
named_year_link (exhibitions list item)
That alone removes a lot of repeated YAML and makes future edits safer.
If you want, next I can map each exact field in your file → the component it should use (a “diff plan”), without rewriting the full config yet.