Skip to main content
vibestrap uses two MDX systems, one for each kind of content:
  • The /docs site (this site, at docs.vibestrap.dev) is on Mintlify. It lives under docs/ and docs/docs.json at the repo root. Setup walkthrough: Deploy docs on Mintlify.
  • The /blog and /changelog routes on the marketing app are on Fumadocs. Their MDX lives under content/blog/ and content/changelog/. This page covers those two.
The split is intentional: docs benefit from Mintlify’s purpose-built navigation / search / AI / multilingual; blog and changelog are part of the marketing site and benefit from being in the same Next.js app (shared theme, shared analytics, locale-aware routing).

How the collections are defined

source.config.ts:
export const blog = defineCollections({
  type: 'doc',
  dir: 'content/blog',
  schema: frontmatterSchema.extend({ /* date, author, categories, … */ }),
});

export const changelog = defineCollections({
  type: 'doc',
  dir: 'content/changelog',
  schema: frontmatterSchema.extend({ version: z.string(), date: ... }),
});

export const pages = defineCollections({
  type: 'doc',
  dir: 'content/pages',
  schema: frontmatterSchema,
});
Each definition lists a dir and a Zod schema for frontmatter. The build generates .source/server.ts, which exports typed arrays consumed by src/lib/source.ts.

Add a new blog post

Blog frontmatter has the most fields:
---
title: Welcome to vibestrap
description: One-line summary.
date: 2026-04-27
author: vibestrap
categories: [release, indie-hacking]
---
Drop the file at content/blog/<slug>.mdx plus <slug>.zh.mdx. The blog index page reads everything via pickByLocale(blog, locale) and sorts by date descending — no extra config needed.

Add a changelog entry

Same shape as blog, with a required version field:
---
title: v1.1 — credit packs
description: Small but mighty.
version: 1.1.0
date: 2026-05-15
---
Files go under content/changelog/. The version field powers the release title and sort order on /changelog.

The pages collection

content/pages/ is a catch-all for one-off MDX pages that don’t belong in blog or changelog. Currently holds a placeholder. Use it when you need MDX rendering without sidebar nav.

Customize MDX components

src/mdx-components.tsx is the central override point for blog + changelog rendering. It defers styling to Tailwind’s prose plugin. Add custom shortcodes here:
export function getMDXComponents(components?: MDXComponents): MDXComponents {
  return {
    Callout: ({ children }) => <div className="callout">{children}</div>,
    ...components,
  };
}
Then <Callout>...</Callout> becomes available in any blog / changelog MDX file. (The Mintlify docs site has its own component library — see Mintlify Components.)

Verify it works

pnpm exec fumadocs-mdx   # regenerate .source/
pnpm dev                 # visit your new post
pnpm build               # catches any frontmatter schema errors
pnpm build runs fumadocs-mdx automatically — invalid frontmatter fails the build with a Zod error pointing at the file.

Common pitfalls

  1. Forgetting to regenerate .source/ — if you add a file and the page 404s in dev, run pnpm exec fumadocs-mdx.
  2. Invalid frontmatter — Zod rejects unknown shapes. Date must parse, version is required for changelog, published: false hides blog posts.
  3. MDX autolinks — writing <https://example.com> breaks the MDX parser. Use [example.com](https://example.com) or bare URL text.
  4. Wrong collection dir — files outside the configured dir are silently ignored. Double-check the path matches source.config.ts.
  5. Don’t put docs here — anything you’d want under /docs belongs in docs/ at the repo root (Mintlify), not content/docs/.

Official docs