Skip to content

Content Management

Marketing content has two sources. General site copy is versioned with the application; posts, authors, categories, and blog media are managed in Sanity.

Content Ownership

ContentSourceChange process
Shared labels, metadata copy, page copy, and repeated calls to actionapps/marketing/src/utils/strings.tsCode review and application deployment
Main navigation and footer link groupsapps/marketing/src/data/site-navigation.tsCode review and application deployment
Structured page-specific dataapps/marketing/src/data/ or the owning feature directoryCode review and application deployment
Route composition and tightly coupled contentapps/marketing/src/app/ and src/components/Code review and application deployment
Blog posts, authors, categories, and imagesSanity documentsDraft, review, and publish in Sanity Studio
CMS field definitions and validationapps/marketing/src/sanity/schemaTypes/Code review, Studio deployment, then content updates
Page metadata and JSON-LD assemblysrc/utils/metadata/ and src/utils/schema/Code review and application deployment

Use the narrowest source that still keeps repeated content consistent. A label used by several components belongs in strings.ts; a navigation destination belongs in site-navigation.ts; a one-off section can keep its copy with the component. Do not introduce a second content registry for the same text.

Code-Driven Content

src/utils/strings.ts contains shared copy for navigation, the footer, page metadata, blog UI, homepage sections, early access, newsletter, contact, demo, and FAQ. Components select the relevant branch rather than embedding another copy of a repeated label.

src/data/site-navigation.ts combines those labels with destinations and descendant-matching behavior:

ts
const siteLinks = {
  product: { label: strings.navigation.links.product, href: '/demo' },
  blog: {
    label: strings.navigation.links.blog,
    href: '/blog',
    matchDescendants: true,
  },
  faq: { label: strings.navigation.links.faq, href: '/faq' },
  contact: { label: strings.navigation.links.contact, href: '/contact' },
}

The exported primary and footer groups feed desktop navigation, mobile navigation, and the footer. Change a destination there so all shell variants remain aligned.

Focused data modules hold larger structured collections. For example, src/data/earlyAccess.ts supplies event-type and preview data to the early-access components. Keep rendering and event behavior in components; keep reusable data and copy out of JSX when several components consume it.

Sanity Content

Sanity is limited to the blog domain. src/sanity/schemaTypes/ defines post, author, category, and Portable Text block schemas. src/sanity/structure.ts configures the Studio navigation, and sanity.config.ts registers the schemas, structure, project, dataset, and /studio base path.

The marketing application does not mount Studio in the App Router. Run the standalone Studio from apps/marketing/:

bash
pnpm exec sanity dev --port 3334

Use the dataset configured for the current environment. Sanity supplies draft and publish workflow, version history, and media management. The site reads published content through the Sanity CDN; it does not expose draft mode or live preview.

Schema Changes

A schema field is not available to the site until every consuming layer understands it:

  1. Update the schema in src/sanity/schemaTypes/.
  2. Update the corresponding type under src/types/blog/.
  3. Add the field to the relevant GROQ projection under src/utils/blog/.
  4. Update metadata, sitemap, or presentation code that consumes it.
  5. Deploy the Studio schema before editors depend on the new field.

For route fetching, filtering, Portable Text, images, metadata, and sitemap behavior, see Blog System.

Metadata Content

Page components use getPageMetadata() instead of constructing unrelated metadata objects inline. Static page titles, descriptions, and keywords come from the metadata branch in strings.ts. Blog-post metadata is derived from the fetched post, including a plain-text excerpt from Portable Text.

Structured data is assembled separately through the schema helpers. Keep visible marketing copy, metadata, and JSON-LD consistent when a page's purpose or claims change. See SEO and Schema.

Updating Content

Code-Driven Pages

  1. Change the existing copy or data source rather than adding a duplicate constant.
  2. Check every component that consumes that source, especially desktop and mobile shell variants.
  3. Preview responsive layouts and verify that longer copy still reflows.
  4. Run the marketing tests, type check, and build for changes that affect rendering or metadata.

Blog Documents

  1. Edit and review the document in the standalone Sanity Studio.
  2. Confirm required relationships, slug, image alt text, and publication date.
  3. Publish the document to make it available through the CDN-backed client.
  4. Check the relevant listing and detail routes, including metadata and sitemap output.

The current client does not configure an ISR interval, cache tags, or on-demand revalidation. Plan content freshness and draft-preview behavior together before adding any of those mechanisms.

Built with ❤️ by the Jubiloop team