Skip to main content

Advanced

Migrating from a custom MDX setup

You already use MDX — via Next.js + next-mdx-remote, Astro, Docusaurus, Contentlayer, or a hand-rolled pipeline. Here's the path from custom MDX to NookDocs without losing content, URLs, or tooling you like.

If you're reading this, you probably have a working custom MDX-based docs setup — maybe Next.js + next-mdx-remote, maybe Astro's content collections, maybe Contentlayer, maybe your own pipeline. It works. The question is: why migrate at all?

This guide is for teams who've decided "yes, moving to NookDocs makes sense" and need the playbook. It's the sibling of migration playbooks but narrower — specifically about moving from one MDX setup to another, preserving the bits you already got right.

What NookDocs brings vs custom MDX

Component library you don't maintain

47 components + docs + theme tokens. Custom MDX requires you to build + maintain all of these yourself.

Managed rendering + hosting

No Next.js config to maintain, no deploy pipeline to tune, no edge caching to debug. Trade: less control.

Dashboard editor

Non-technical writers edit without a dev environment. Custom setups require local dev or Netlify-CMS-style additions.

Built-in AI affordances

/llms.txt, .md URLs, Copy-for-LLMs, AI assistant, RSS feeds, contextual menu. Custom MDX doesn't ship any of this by default.

What you give up

Custom React components in MDX

NookDocs accepts the built-in component set + aliases. Your custom <PricingWidget> doesn't render (unless you go headless).

Custom build hooks

Pre-build scripts that generated docs from OpenAPI, TypeScript types, or metadata files. NookDocs handles OpenAPI + schema-reference generation natively; custom scripts migrate to CI.

Custom layouts per-page

"This page has a right-sidebar, that one has a 2-column grid" needs adapting to NookDocs's layout system (see custom-layouts).

Full deploy-pipeline control

You stop managing Vercel / Netlify / CloudFront / Lambda@Edge. Some teams miss this; most don't.

Content audit (before any code moves)

Before touching configuration, audit your existing content:

List every MDX file

find docs -name "*.mdx" | wc -l — know how many pages you're migrating. Typical mid-sized site: 100-300.

Inventory custom components used

Grep every <CapitalisedComponent> tag across your MDX. Build a frequency table.

grep -rho '<[A-Z][A-Za-z]*' docs/ | sort | uniq -c | sort -rn

Map each to NookDocs equivalent. Components with no equivalent need: (a) substitute to a closest-match NookDocs component, (b) inline their rendered output, or (c) use <Visibility for="humans"> wrap to keep custom content for web readers while stripping for LLMs.

Inventory front-matter fields

What frontmatter fields do you use? NookDocs supports: title, description, icon, hidden, rss, sitemap, openapi, timestamp, plus forward-compatible unknown fields. Extra fields stay in the MDX but won't drive behaviour.

Inventory build-time magic

Do you currently inject data at build? Auto-generate certain pages? Use @next/mdx features like getStaticProps-per-page? Each one needs a migration path.

Inventory non-MDX content

Static assets (images, PDFs, videos) and custom CSS / fonts migrate into the public/ folder of your NookDocs repo — CSS also has a customCss config key.

Custom scripts do not have a path. NookDocs injects analytics and support widgets from a fixed list of supported integrations, and the site's Content Security Policy allows exactly those hosts and no others — a <script> you add yourself is blocked in the reader's browser. If you depend on a provider that is not on that list, ask us and we will add it.

Mapping custom components to NookDocs

Common component mappings:

Custom componentNookDocs equivalentNotes
<Alert type><Callout type>Same API usually works as-is
<InfoBox> / <NoteBox><Note>Direct swap
<Tabs> + <Tab><Tabs> + <TabsList> + <TabsTrigger> + <TabsContent>More verbose; migration script can rewrite
<CodeTabs><CodeGroup>Direct swap; language comes from fence tag
<Steps> + <Step><Steps> + <Step title>Direct swap; some custom setups use number instead of title — rename
<ApiParam><ParamField>Prop names may differ; see schema-reference
<Details> / <Expand><Expandable> or <Accordion><Expandable> is lighter; <Accordion> groups better
<Image src alt><Image> or <Frame caption><Frame> wraps with a caption + border
<Video> / <Embed><Video> / <Embed>Usually direct swap
<FileTree> / <Tree><FileTree>Same API
<Mermaid><Mermaid>Same; content is inline Mermaid source

Custom components with no good equivalent go one of three ways:

  1. Rewrite content — instead of <PricingWidget>, write a markdown table.

  2. Inline rendered output — screenshot the widget, inline it with <Frame>.

  3. Drop + simplify — the widget was probably over-engineered; plain prose works.

The migration steps — specific to custom MDX

Clone your existing repo to a scratch directory

Keep it as a read-only reference. All migration work happens in a new repo.

Run the NookDocs CLI against the scratch directory

nookdocs migrate ./my-custom-mdx-docs --from mintlify

Even though it's not Mintlify, the --from mintlify path does the most work (maps docs.json-style config + does component rewriting). Adjust manually afterwards.

(If Docusaurus / Astro / Contentlayer support lands in the CLI, use that — see migrate command.)

Manually port your config

Map your custom config fields to nookdocs.config.json. Navigation, theme colours, logo, integrations, SEO settings. Use the schema reference as the canonical field list.

Port custom components

For each unmapped custom component, decide: rewrite, inline, or drop. Batch by component type — "rewrite every <PricingWidget> first, then every <TeamList>, …".

Port build-time scripts

If you have pre-build scripts that generate content, adapt them to NookDocs's flow:

  • OpenAPI auto-gen → use NookDocs's built-in OpenAPI support (point api.openapi at the spec file).

  • Custom schema-reference → replace with the built-in generator at scripts/generate-schema-reference.mjs.

  • Other generators → port to a scripts/generate-*.mjs file + wire into npm run build.

Port assets

Images, PDFs, videos go under docs/images/ etc (or public/ if served from the tenant root). Relative paths in MDX should resolve to the same location.

Set up redirects

URL structure might shift during migration. Build the redirect map. Commit to nookdocs.config.json#redirects.

Smoke test on staging

Deploy to a staging subdomain. Click through every top-level page. Test search. Test the AI assistant. Compare against the old site side-by-side.

Cutover

DNS flip or CDN rewrite. Traffic reaches NookDocs; redirects from old URLs route. See migration playbooks for cutover mechanics.

Specific platform notes

From Next.js + next-mdx-remote

  • Your pages/[...slug].tsx route becomes unnecessary — NookDocs handles routing.

  • serialize() + MDXRemote happen server-side on NookDocs; no-op for you.

  • remark / rehype plugins: built-in behaviour usually covers the common ones (syntax highlighting, frontmatter). Custom ones don't migrate.

  • Middleware for auth / rewrites: rebuild at the NookDocs layer via config.redirects + per-page hidden:true.

From Docusaurus

  • Docusaurus-specific <Tabs> / <TabItem> → NookDocs <Tabs> + children shape.

  • Sidebar config in sidebars.jsnookdocs.config.json#navigation.

  • Docusaurus versioning (versioned_docs/) → NookDocs versioning.

  • Algolia DocSearch → built-in Postgres FTS replaces, unless you specifically want Algolia features.

  • React components in MDX → most won't render; port or drop.

From Astro content collections

  • Astro's schema validation in config.ts → NookDocs validates at sync time via src/lib/config/schema.ts.

  • Astro's .astro components in MDX → don't render; rewrite.

  • Content collection frontmatter → most fields map 1:1; extras preserved but inert.

  • Astro's image optimisation → NookDocs serves images via /api/assets/; optimisation is on the roadmap, not shipping yet.

From Contentlayer

  • contentlayer.config.ts definitions → map to nookdocs.config.json.

  • Build-time Contentlayer transforms → port to scripts/*.mjs + npm run build chain.

  • Generated TypeScript types → NookDocs has public/schema.json for config types; content pages don't have generated types (they're just MDX).

From a hand-rolled MDX pipeline

Usually easier than migrating from a framework, because you already understand the moving parts. Main decisions:

  • Keep your OpenAPI spec? Yes — point api.openapi at it.

  • Keep your custom plugins? Probably drop — they're hard to port and usually replicate something NookDocs already does.

  • Keep your search? Replace with built-in unless you have a specific reason.

Pitfalls specific to MDX-to-MDX migration

What's easier AFTER migration

  • Non-dev writers can edit without a dev environment.

  • AI affordances "just work"/llms.txt, .md URLs, Copy-for-LLMs dropdown, RSS feeds on any page.

  • Component library stops being your job — upgrades + a11y fixes + dark-mode tuning all live in NookDocs.

  • Deploys are automatic — push to GitHub, ~30s later it's live.

  • Search is built-in — Postgres FTS with on-site ⌘K, no external service.

When to NOT migrate

  • If you've built significant custom tooling (plugins / layouts / generators) that would take weeks to reimplement.

  • If your content has exotic React components central to the experience.

  • If you have a team of engineers who actively enjoy maintaining the docs infra.

In those cases, headless / custom frontend is a middle path: use NookDocs as the content store + editor + API, render yourself.

Related

Was this page helpful?

Last updated August 30, 2026

Migrating from a custom MDX setup | NookDocs | NookDocs