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 -rnMap 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 component | NookDocs equivalent | Notes |
<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:
Rewrite content — instead of
<PricingWidget>, write a markdown table.Inline rendered output — screenshot the widget, inline it with
<Frame>.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 mintlifyEven 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.openapiat 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-*.mjsfile + wire intonpm 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].tsxroute becomes unnecessary — NookDocs handles routing.serialize()+MDXRemotehappen server-side on NookDocs; no-op for you.remark/rehypeplugins: 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-pagehidden:true.
From Docusaurus
Docusaurus-specific
<Tabs>/<TabItem>→ NookDocs<Tabs>+ children shape.Sidebar config in
sidebars.js→nookdocs.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 viasrc/lib/config/schema.ts.Astro's
.astrocomponents 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.tsdefinitions → map tonookdocs.config.json.Build-time Contentlayer transforms → port to
scripts/*.mjs+npm run buildchain.Generated TypeScript types → NookDocs has
public/schema.jsonfor 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.openapiat 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,.mdURLs, 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
Migration playbooks — generic phased migration strategy
Migrate from Mintlify (CLI) — closest to the mechanical conversion flow
Custom frontend — alternative if you want to keep custom rendering
Schema reference — target config shape
Component library — target component set