Skip to main content
The page document is the primary content unit for the Capstone site. Every URL-addressable page — home, sponsors listing, events, and any custom page — is a page document assembled from an ordered array of content blocks.

Fields

Layout group

title
string
required
Human-readable page title. Displayed in the browser tab and used as the source for auto-generating the slug.
slug
slug
required
URL path for the page. Auto-generated from title (max 96 characters). Used by [...slug].astro to match incoming routes and by ALL_PAGE_SLUGS_QUERY for static path generation.
site
string
Multi-site discriminator. Hidden on the production (Capstone) dataset. Required on the rwc dataset — must be rwc-us or rwc-intl.
template
string
Page layout template. Defaults to default. Controls which template component wraps the block renderer.
ValueTemplate component
defaultDefaultTemplate.astro
fullWidthFullWidthTemplate.astro
landingLandingTemplate.astro (hides nav)
sidebarSidebarTemplate.astro
twoColumnTwoColumnTemplate.astro

SEO group

seo
seo object
Embedded SEO object with metaTitle, metaDescription, and ogImage. Falls back to title and siteDescription from Site Settings when not set.

Content group

blocks
array of block objects
Ordered list of content blocks that make up the page body. The insert menu groups blocks into categories:
  • Heroes: heroBanner
  • Content: richText, textWithImage, faqSection, articleList, timeline, pullquote
  • Display: teamGrid, imageGallery
  • Media & Stats: statsRow, featureGrid, videoEmbed
  • Social Proof: sponsorCards, projectCards, logoCloud, sponsorSteps, testimonials, eventList, sponsorshipTiers
  • Data: comparisonTable
  • Calls to Action: ctaBanner, contactForm
  • Utility: divider, announcementBar

Template compatibility validation

The blocks field includes a custom validation rule that warns editors when a wide block (e.g. heroBanner, sponsorCards) is placed on a constrained-column template (sidebar, twoColumn). Incompatible combinations produce a Studio warning — they are still publishable.

Fetching pages: PAGE_BY_SLUG_QUERY

Pages are fetched by slug using PAGE_BY_SLUG_QUERY, defined in astro-app/src/lib/sanity.ts:
*[_type == "page" && slug.current == $slug && ($site == "" || site == $site)][0]{
  _id,
  title,
  "slug": slug.current,
  template,
  seo { metaTitle, metaDescription, ogImage { … } },
  blocks[]{
    _type, _key, backgroundVariant, spacing, maxWidth, variant,
    _type == "heroBanner" => { heading, subheading, backgroundImages, ctaButtons, alignment },
    _type == "richText" => { content[]{ … } },
    // … one branch per block type
  }
}
The $site parameter short-circuits on the production dataset (empty string), so all pages match regardless of the site field value.

Dynamic routing via [...slug].astro

astro-app/src/pages/[...slug].astro handles all page routes:
1

getStaticPaths

Fetches all published page slugs via ALL_PAGE_SLUGS_QUERY, then calls prefetchPages() to batch-fetch all page data in parallel (6 at a time) and populate the module-level cache.
2

Per-page render

Calls getPage(slug) — returns instantly from the cache. Resolves the template component from the template field value.
3

Block resolution

Fetches sponsors, projects, testimonials, and events once each (getAllSponsors(), etc.) and passes them to <BlockRenderer> as props. Block-level data is resolved client-side from these pre-fetched arrays.
In Visual Editing mode, getStaticPaths is bypassed. The page is rendered as a server island (SanityPageContent server:defer) that fetches fresh draft data on every request.

Build docs developers (and LLMs) love