Marketing pages
Public landing, pricing from plans, and an MDX blog + changelog.
Marketing adds the public-facing surface: a landing page, a pricing page derived
straight from the plan config, and an MDX-backed blog and changelog. Content is
files, not database rows — content-collections
compiles src/content/** into typed collections at build time.
Optional feature
Marketing is optional (core: false, requires: ["billing"]). The pricing
page renders billing's PlanPicker off config/plans.ts, so plans stay the
single source of truth. It is a leaf — no feature depends on it.
What it does
- Landing page —
MarketingLandinginsideMarketingShellrenders at the core root/behind@feature:marketingmarkers (a pruned/falls back toredirect("/dashboard")). - Pricing page —
/pricingrenders billing'sPlanPickeroff@/config/plans(PLANS). Marketing supplies only CTA derivation (pricingCtaHref/pricingCtaLabel): a free tier links to/sign-up, a priced plan to/sign-up?returnTo=/settings/billing. No pricing is duplicated marketing-side. - Blog —
/blogand/blog/[slug], thin server shells overBlogIndex/BlogPostreadinglistBlogPosts()/getBlogPost(slug)(lib/blog.ts). Posts are static (generateStaticParamsover the build-time collection). - Changelog —
/changelogand/changelog/[slug], mirroring the blog vialib/changelog.ts. - SEO, sitemap, robots, OG images —
marketingMetadata(...)andabsoluteUrl(...)(lib/seo.ts),marketingSitemap()(lib/sitemap.ts), andogImageElement(...)(one PNG per post vianext/og, no fonts fetched so the build stays offline). The core-rootsitemap.ts/robots.tscall these behind markers and fall back to an empty sitemap / disallow-all when pruned.
Models it adds
None. Marketing wraps nothing in prisma/schema.prisma — blog and changelog
content are MDX files under src/content/**, compiled into typed collections
(allPosts, allChangelogs) rather than stored in the database.
Env it needs
None. Adds four dependencies — @content-collections/core, /next, /mdx, and
@tailwindcss/typography — wired via withContentCollections(...) in
next.config.ts and the @plugin "@tailwindcss/typography" directive in
globals.css. content-collections is the template's content stack for
apps/web (the docs site in apps/docs uses a separate fumadocs stack).
Composition
requires: ["billing"]— the pricing page importsPLANSfrom@/config/plansandPlanPickerfrom@/features/billing; the boundary rule permits that deep import precisely because marketing declares billing. Billing owns the plan config and picker; marketing supplies only the crawlable acquisition CTAs (deep-links into sign-up rather than in-page mutations).- Dependents — none; marketing is a leaf.
What pruning removes
Leaving marketing out drops the whole (marketing) route group (pricing, blog,
changelog, layout, OG-image routes), features/marketing, the src/content
MDX, content-collections.ts and its vitest global setup, and the four
content-collections / typography dependencies. The shared core-root files that
carry marketing markers stay but revert to their fallbacks: / redirects to
/dashboard, sitemap.ts returns empty, and robots.ts disallows all. Because
marketing is a leaf, pruning it removes only its own surface — nothing else
breaks.