Skip to content
Blume
Esc
↑↓navigate↵open⌘Jpreview
Guides

Migrate

Migrate your docs from Mintlify

Hand your Mintlify repository to a coding agent, review what it changed, keep your URLs working, and deploy a docs site you host yourself.

By 7 min read

By the end of this guide, your Mintlify docs are a Blume project in your own repository: the same MDX pages, navigation rebuilt as folders and tabs, a redirect for every URL that moved, and a site deployed on a host you choose. A coding agent does the conversion, and you review it.

This moves your content to Blume, an open-source docs framework. It doesn't self-host Mintlify's own renderer, which Mintlify offers separately on its Enterprise plan.

What carries over

Most of a Mintlify site maps straight across, because Blume reads the same MDX and ships most of the same components. Cards, Steps, Columns, Frame, Tooltip, Expandable, and the API field components render as they are. The rest changes shape:

In MintlifyIn Blume
docs.json (or mint.json)blume.config.ts, plus folders for navigation
Navigation groupsFolders, with a meta.ts where order or labels need it
Top-level tabsHeader tabs, one folder each
AnchorsFeatured links, pinned above the sidebar
Navbar links and the primary buttonnavigation.actions and navigation.cta in the header
<Note>, <Tip>, <Warning>:::note, :::tip, :::warning
<AccordionGroup> of <Accordion><Accordion> of <AccordionItem>
<Tabs><Tabs inline>, which keeps Mintlify's borderless look
Snippets in /snippets<include> partials
Font Awesome iconsLucide icons
Asset folders at the root, like /imagesThe same folders under public/, at the same URLs
An OpenAPI spec in docs.jsonopenapi() in reference, with a page per operation

Some things have no Blume equivalent, and the agent reports them rather than faking them. The ones that matter most are Mintlify's web editor (in Blume, pages are files in Git), reader authentication (use your host's password or SSO protection instead), and Mintlify-hosted extras like its analytics dashboard. The Blume vs Mintlify page covers when staying on Mintlify is the better call.

Before you start

The agent edits your repository in place, so start from a clean working tree on a new branch. That way the whole migration is one diff you can review, and throwing it away is one command.

git switch -c migrate-to-blume

Next, save the URLs your site serves today. You'll check every one of them against the new site later, so no link or bookmark breaks. Mintlify publishes a sitemap, so pull the paths out of it and keep the list outside the repository:

curl -s https://docs.acme.example/sitemap.xml \
  | grep -o '<loc>[^<]*' \
  | sed -e 's#<loc>https://docs.acme.example##' -e 's#\(.\)/$#\1#' \
  > ../old-urls.txt

Replace docs.acme.example with your docs domain in both places. The last expression drops any trailing slash, since Blume's URLs never end in one. Finally, check that you have Node.js 22.12 or later, and that Claude Code or Codex is installed and signed in.

Run the migration

From the root of your Mintlify repository, run:

npx blume migrate mintlify --claude

To use Codex, swap --claude for --codex. On pnpm 12, run it with pnpm dlx --allow-build=esbuild instead of npx, since pnpm 12 won't run esbuild's install script until you approve it. Leave out mintlify and Blume detects the source from your docs.json or mint.json. Run it with neither flag and it only prints where the skill lives, for another agent to use.

blume migrate converts nothing itself. It names the source, then opens the agent in your terminal on the blume-migrate skill that ships inside the package, pointed at the skill's Mintlify reference. The agent runs interactively, so its edits go through its usual permission prompts. Following the skill, it:

  1. Writes blume.config.ts, carrying over only what your docs.json sets.
  2. Moves pages into folders and tabs that match your navigation.
  3. Runs a codemod over your frontmatter that maps Font Awesome icons to Lucide and renames or drops keys Blume doesn't accept.
  4. Rewrites components, turns snippets into includes, and moves assets into public/.
  5. Adds a redirect for every URL that moves.
  6. Swaps Mintlify for Blume in package.json, or creates one if the repository has none, then runs blume build and blume validate until both pass.

It finishes with a summary of what it migrated, dropped, and approximated. Keep it: it's your checklist for the review.

Review the diff

Start with git diff --stat for the shape of the change, run npx blume dev, and work through it in this order.

  • Config. blume.config.ts should hold only what your docs.json set: the title, logo, colors as theme.accent, redirects, and analytics. Anything left out falls back to a Blume default on purpose.
  • Navigation. Click through every tab. Each Mintlify tab should be a header tab over its own folder, anchors should sit at the top of the sidebar, and header links should be in the header.
  • Pages. Spot-check a page from each group, especially any that used snippets, accordions, or tabs.
  • Assets. Images should load from public/ at the same paths as before.
  • Dependencies. package.json should run blume dev and blume build, with Mintlify gone and Blume added.

When a nested group flattens

The conversion most worth checking by hand is a group nested inside another group. In Mintlify, that nesting often lives only in docs.json, while the pages sit side by side in one folder:

{
  "group": "Webhooks",
  "pages": [
    "webhooks/overview",
    {
      "group": "Reference",
      "pages": ["webhooks/events", "webhooks/signatures"]
    }
  ]
}

Blume builds the sidebar from folders, so if those files stay where they are, the Reference group disappears and its pages join Webhooks. The agent should have either moved them into a subfolder or told you it didn't. To keep the group, move the pages into a folder named after it and add a redirect for each moved URL:

mkdir -p webhooks/reference
git mv webhooks/events.mdx webhooks/reference/events.mdx
git mv webhooks/signatures.mdx webhooks/reference/signatures.mdx
redirects: [
  { from: "/webhooks/events", to: "/webhooks/reference/events", status: 308 },
  { from: "/webhooks/signatures", to: "/webhooks/reference/signatures", status: 308 },
],

If you'd rather not move any URLs, an explicit navigation.sidebar can nest the pages where they are. It replaces the whole generated sidebar, though, so moving the files is usually the smaller change.

Check every old URL

With npx blume dev running, walk the list you saved and print every old URL that no longer lands on a page:

while read -r path; do
  code=$(curl -sL -o /dev/null -w "%{http_code}" "http://localhost:4321$path")
  [ "$code" = "200" ] || echo "$code $path"
done < ../old-urls.txt

Redirects count as passing, since -L follows them to the page. For each path it prints, add a redirect to blume.config.ts. Mintlify's permanent redirects map to status: 308, and a pattern like /beta/:slug* covers a whole section in one entry. Run the loop again until it prints nothing. For status codes, patterns, and what each host does with them, see Move documentation URLs while preserving old links.

Build and validate

Stop the dev server, then build the site and check every link in it:

npx blume build
npx blume validate --strict

blume build fails on any page, route, or config error. Then blume validate --strict checks every internal link, heading anchor, and asset, and treats warnings as failures too. Add --external to check outbound links as well. Fix what they report rather than reaching for --no-strict, which builds anyway and silently drops the pages that fail.

Deploy

Blume deploys to any host. This guide uses Vercel, because Blume detects the site URL there and the vercel() adapter answers your redirects with real HTTP status codes. Add it to your config:

import { defineConfig } from "blume";
import { vercel } from "blume/deploy";

export default defineConfig({
  // ...everything the migration wrote
  deployment: vercel(),
});

Commit, push the branch, and import the repository in Vercel. Set the project's Node.js version to 22 or later. Every branch gets a preview deployment, so the migration branch is live at a preview URL before anything touches your domain.

Run the URL check from the last section against the preview URL. If your previews are protected by Vercel Authentication, curl gets a login page instead, so check a sample of old URLs in the browser now and run the full loop against production after the switch.

For another host, see Deployment. On a static host, redirects are served as redirect pages plus the file your host reads, so check which one yours uses.

Switch over

Merge the branch, add your docs domain to the Vercel project, and update the DNS record that points it at Mintlify so it points at Vercel instead. Leave the Mintlify project running until the new site has served traffic for a while and the URL check passes on production. Rolling back is then a matter of changing the DNS record back.

Your redirects carry old links and bookmarks to the new pages. Search engines follow permanent redirects too, but they recrawl on their own schedule, so submit the new sitemap.xml in Google Search Console and give results time to catch up.

What you run now

Moving off a hosted platform means a few things become yours. Builds and hosting run on your own account. If you turn on the assistant, it runs on a model provider you choose and bills to your key. Analytics come from an adapter you pick. Private docs rely on your host's protection. The pricing page covers what that costs in practice, and Specific's story shows one team's move from Mintlify.

Troubleshooting

The build fails on a page's frontmatter

Blume's frontmatter schema is strict, so a key Mintlify accepted but Blume doesn't is a build error. BLUME_FRONTMATTER_INVALID names the page and key: map it to a Blume key or remove it. The codemod handles the common ones, so these are usually custom keys.

An icon is missing

An icon name Lucide doesn't have renders nothing. Navigation icons warn with BLUME_UNKNOWN_ICON, but an icon on a component like a card fails silently. Look each name up on lucide.dev.

The API reference is missing, or the build can't load the spec

The reference doesn't add a header tab on its own: add a tab to navigation.tabs that points at its route. If the spec is a URL that can't be fetched, blume build fails with BLUME_OPENAPI_UNAVAILABLE, and blume dev warns and leaves the reference out. Download the spec into the repository and point spec at the file, so a build never depends on the network.

Links to API endpoints 404

Blume builds operation URLs from the tag and operation ID, like /api-reference/messages/get-message, which rarely match Mintlify's. blume validate catches internal links to fix; add redirects for the old endpoint URLs other sites link to.

A redirect fails the build

BLUME_REDIRECT_MATCHES_PAGE means a redirect pattern also covers a page you kept. Narrow the pattern so it only matches the URLs that moved.

Next step

Migrate your docs

Run it at the root of your Mintlify repository, on a clean branch, then work through the review above.

npx blume migrate mintlify --claude
Read the migration reference

A step here not working for you? Report a broken step.

Keep going.More guides.

  • Migrate your docs from Docusaurus

    Move a Docusaurus site's docs to Blume with a coding agent, and check the sidebars, admonitions, and React components it carries over.

  • Migrate your docs from Fumadocs

    Hand your Fumadocs repository to a coding agent, check its meta.ts and component rewrites, keep your docs at /docs, and deploy with every old URL working.

  • Migrate your docs from Starlight

    Move a Starlight site to Blume with a coding agent, keep its pages and URLs, and rebuild the overrides and splash pages that stay Astro work.

Upgrade your docs with Blume.

Install today and ship a production-grade docs site in minutes. Free and open source, forever.

npx blume init