Skip to Content
DocumentationBuild a websitePublish status & environments

Publish status & environments

Not every page in the registry should render on every build. A page moves through a lifecycleplannedwritingreviewreadypublished — and which statuses are visible is a property of the environment, not the SSG. Production shows only published; a preview build also shows sealed review/ready work so editors can see it before it goes live.

It’s config, not SSG logic

The status→environment policy lives in gtmesh.config.yaml, and the engine’s render-manifest applies it. The mistake to avoid is hard-coding the same rules in the SSG — now the policy lives in two places and drifts. Instead, the content-types package exposes the same policy:

# gtmesh.config.yaml environments: local: { renders: [writing, review, published] } dev: { renders: [writing, review, published] } prod: { renders: [published] }
import { renderableStatuses, RENDER_ENVS, viewForEnv } from "@your-project/content-types"; renderableStatuses("prod"); // ["published"] renderableStatuses("dev"); // ["writing", "review", "published"] viewForEnv("prod"); // "live" — the manifest link VIEW for this env viewForEnv("dev"); // "preview"

renderableStatuses(env) reads config.environments[env].renders — byte-for-byte the set render-manifest --env <env> uses. There is no second copy to keep in sync. viewForEnv(env) is its sibling for links: it maps the same env to the manifest link view (live = the published/prod render set, preview = wider), so loadPages({ view: viewForEnv(env) }) gives every page.links.* the right set for this build — the SSG never branches on live/preview.

renderableStatuses throws on an environment that isn’t configured — a typo shouldn’t silently render nothing. The configured set is RENDER_ENVS.

Filtering the render set

Every list/load helper takes a status filter. Feed it the environment’s set and you get exactly the pages that should render:

const env = process.env.DEPLOY_ENV ?? "prod"; const statuses = renderableStatuses(env); // generateStaticParams / routes: const slugs = pageSlugs({ status: statuses }); // a typed section listing: const guides = loadPagesByType<GuidePage>("guide", { status: statuses });

Prune edges too. A page hidden in this environment shouldn’t be linked to. When you resolve a related slug or a hub member, drop any whose page isn’t in the render set — otherwise a prod build links to a review-only page that 404s. Build a Set of the renderable slugs once and test membership as you traverse.

The alternative: read the manifest

If you’d rather not import the package for this — or you’re building in a non-TypeScript stack — gtmesh render-manifest --env <env> writes the renderable slug set to .gtmesh/render-manifest.<env>.json. Same policy, emitted as a file your build step can read directly.

Last updated on