Spine pieces in MDX, leaves in raw strings
On this surface the six spine pieces (overview, architecture, slug-strategy, image-pipeline, sitemap-automation, results) are MDX files in content/case-study. Each can embed React components: a code excerpt, an inline diagram, a callout. The 22 leaves are raw markdown strings in the bodySections field of the slug manifest; they cannot embed React components. The split is deliberate: spine pieces are editorial and benefit from richness; leaves are templated and benefit from the simpler validator pass. The build-time validator runs against the raw strings in seconds; running it against MDX would require parsing the MDX AST.
The build-cost numbers
MDX compilation adds about twenty-eight to thirty-five milliseconds per file on our Docker build host. Across six spine pieces that is two hundred milliseconds total, negligible against the rest of the Next.js build. Scaled to a hundred MDX files, the overhead is three seconds; to a thousand files, it is half a minute. The cross-over against raw strings (which compile at roughly two milliseconds per file via JSON.parse) happens around two hundred files, which is past the threshold where we recommend the CMS path anyway.
The hybrid pattern when you need both
If a specific leaf needs a component (a comparison table, an interactive diagram, an embedded form), the right move is to break that single leaf into its own page component rather than upgrading the leaf manifest to MDX. We have done this twice on this surface: the /audit page is a custom page with form components, and the /case-study spine pages are MDX. Both bypass the leaf template entirely. Keeping the leaf template simple preserves the build-time validator's invariants and avoids the editorial inconsistency that comes from leaves that look superficially similar but have different feature sets.
