React + Vite
Integrate MDX UI with a React + Vite project using @mdx-js/rollup and MDXProvider
Overview
Vite has no built-in MDX convention like Next.js, so you wire things up manually — @mdx-js/rollup handles MDX compilation, and MDXProvider from @mdx-js/react injects your components globally.
Best for: SPAs, dashboards, internal tools, interactive docs without SSR.
1. Create a Vite + React Project
Skip this step if you already have a project.
pnpm create vite my-app --template react-ts
cd my-app
pnpm install2. Install MDX Support
pnpm add @mdx-js/rollup @mdx-js/react remark-gfm
pnpm add -D @types/mdx3. Configure Vite
Update vite.config.ts:
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import mdx from "@mdx-js/rollup";
import remarkGfm from "remark-gfm";
export default defineConfig({
plugins: [
mdx({
remarkPlugins: [remarkGfm],
providerImportSource: "@mdx-js/react",
}),
react(),
],
});Add MDX type declarations to src/vite-env.d.ts:
/// <reference types="vite/client" />
declare module "*.mdx" {
import type { ComponentType } from "react";
const Component: ComponentType;
export default Component;
}4. Install Tailwind CSS
MDX UI components use Tailwind CSS for styling.
bash pnpm add tailwindcss @tailwindcss/vite
Update vite.config.ts:
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import mdx from "@mdx-js/rollup";
import remarkGfm from "remark-gfm";
import tailwindcss from "@tailwindcss/vite";
export default defineConfig({
plugins: [
mdx({ remarkPlugins: [remarkGfm], providerImportSource: "@mdx-js/react" }),
react(),
tailwindcss(),
],
});Add at the top of src/index.css:
@import "tailwindcss";
/* rest of your existing styles stay below */If you have existing
@tailwind base,@tailwind components, or@tailwind utilitieslines, remove those and use this single import instead.
5. Initialize MDX UI
bash pnpm dlx @ravikumarsurya/mdx-ui init
This creates mdx-ui.json and installs the cn() utility.
6. Add Components
bash pnpm dlx @ravikumarsurya/mdx-ui add callout accordion code-block
The CLI automatically copies the component file and updates
src/components/mdx-components.tsx with the correct imports and mappings. You
never need to edit that file manually.
7. Set Up MDXProvider
The init command creates src/components/mdx-components.tsx with the marker comments the CLI uses to auto-patch it. You only need to wire it up once in src/main.tsx — every subsequent add command keeps it up to date.
Wrap your app with MDXProvider in src/main.tsx:
import React from "react";
import ReactDOM from "react-dom/client";
import { MDXProvider } from "@mdx-js/react";
import { mdxComponents } from "./components/mdx-ui/mdx-components";
import App from "./App";
import "./index.css";
ReactDOM.createRoot(document.getElementById("root")!).render(
<React.StrictMode>
<MDXProvider components={mdxComponents}>
<App />
</MDXProvider>
</React.StrictMode>,
);8. Set Up Routing
If your app has multiple MDX pages, use React Router to map URLs to files.
Install React Router
bash pnpm add react-router-dom Create your MDX pages
Create these three files with some starter content:
Wire up routes in src/App.tsx
import { BrowserRouter, Routes, Route } from "react-router-dom";
import { MDXProvider } from "@mdx-js/react";
import { mdxComponents } from "./components/mdx-components";
import Index from "./content/index.mdx";
import About from "./content/about.mdx";
import Guide from "./content/guide.mdx";
export default function App() {
return (
<MDXProvider components={mdxComponents}>
<BrowserRouter>
<Routes>
<Route path="/" element={<Index />} />
<Route path="/about" element={<About />} />
<Route path="/guide" element={<Guide />} />
</Routes>
</BrowserRouter>
</MDXProvider>
);
}Move
<MDXProvider>here and remove it frommain.tsx— one provider wrapping all routes is enough.
Add a Navbar
Create src/components/Navbar.tsx to navigate between your MDX pages:
import { NavLink } from "react-router-dom";
const links = [
{ label: "Home", href: "/" },
{ label: "Guide", href: "/guide" },
{ label: "About", href: "/about" },
];
export function Navbar() {
return (
<nav className="border-b px-6 py-3 flex items-center gap-6">
<span className="font-bold text-sm mr-4">My App</span>
{links.map(({ label, href }) => (
<NavLink
key={href}
to={href}
end={href === "/"}
className={({ isActive }) =>
isActive
? "text-sm font-medium text-green-600"
: "text-sm text-gray-500 hover:text-gray-900"
}
>
{label}
</NavLink>
))}
</nav>
);
}Add <Navbar /> to the src/App.tsx from the previous step:
import { BrowserRouter, Routes, Route } from "react-router-dom";
import { MDXProvider } from "@mdx-js/react";
import { mdxComponents } from "./components/mdx-ui/mdx-components";
+ import { Navbar } from "./components/Navbar";
import Index from "./content/index.mdx";
import About from "./content/about.mdx";
import Guide from "./content/guide.mdx";
export default function App() {
return (
<MDXProvider components={mdxComponents}>
<BrowserRouter>
+ <Navbar />
+ <main className="max-w-3xl mx-auto px-6 py-10">
<Routes>
<Route path="/" element={<Index />} />
<Route path="/about" element={<About />} />
<Route path="/guide" element={<Guide />} />
</Routes>
+ </main>
</BrowserRouter>
</MDXProvider>
);
}
NavLinkfrom React Router automatically applies the active class when the URL matches — no manualuseLocationneeded.
Handle client-side routing in Vite
By default, Vite's dev server only serves index.html for /. Deep links like /guide will 404 on refresh without this fix.
Add the server block to your existing vite.config.ts:
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import mdx from "@mdx-js/rollup";
import remarkGfm from "remark-gfm";
export default defineConfig({
plugins: [
mdx({ remarkPlugins: [remarkGfm], providerImportSource: "@mdx-js/react" }),
react(),
],
+ server: {
+ historyApiFallback: true,
+ },
});For production, configure your host (Vercel, Netlify, Nginx) to redirect all paths to index.html:
Project Structure
Math Components
Math uses pure JSX primitives — no KaTeX or LaTeX required. Install and add to components:
pnpm dlx @ravikumarsurya/mdx-ui add math-primitives math-solution// src/components/mdx-components.tsx
import {
Frac,
Pow,
Integral,
Sum,
Alpha /* ...all primitives */,
} from "./mdx-ui/math-primitives";
import {
Solution,
SolutionStep,
SolutionAnswer,
SolutionNote,
} from "./mdx-ui/math-solution";
export const mdxComponents = {
// ...existing
Frac,
Pow,
Integral,
Sum,
Alpha,
Solution,
SolutionStep,
SolutionAnswer,
SolutionNote,
};See Math Primitives for the full list of 370+ components.
Troubleshooting
Cannot find module @/lib/primitives or @/lib/motion
MDX UI components use the @/ path alias. You need to configure it in vite.config.ts and in every tsconfig that TypeScript compiles directly.
1. Install the Node path helper:
bash pnpm add -D @types/node 2. Add the alias to vite.config.ts:
+ import path from "path";
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import mdx from "@mdx-js/rollup";
import remarkGfm from "remark-gfm";
export default defineConfig({
plugins: [
mdx({ remarkPlugins: [remarkGfm], providerImportSource: "@mdx-js/react" }),
react(),
],
+ resolve: {
+ alias: {
+ "@": path.resolve(__dirname, "./src"),
+ },
+ },
});3. Add the alias to tsconfig.app.json (and tsconfig.json if present):
Vite projects use TypeScript project references. tsc -b compiles each reference using its own config file — so paths must be in tsconfig.app.json, not just the root tsconfig.json. If you only add it to the root, the dev server works (Vite has its own resolver) but tsc builds fail.
{
"compilerOptions": {
+ "paths": {
+ "@/*": ["./src/*"]
+ }
}
}baseUrl is deprecated in TypeScript 5.5+. paths works standalone without
it.
After this, @/lib/primitives resolves to src/lib/primitives.ts which the CLI copies during init.
Components not rendering in MDX
Make sure providerImportSource: "@mdx-js/react" is set in vite.config.ts and your app is wrapped in <MDXProvider>.
TypeScript error on .mdx imports
Add the module declaration to vite-env.d.ts:
declare module "*.mdx" {
import type { ComponentType } from "react";
const Component: ComponentType;
export default Component;
}Tailwind classes not applying
Ensure src/**/*.mdx is in your Tailwind content array so component class names are not purged.
Use with Claude Code (MCP)
Once your React + Vite project is set up, you can connect the mdx-ui MCP server so Claude knows exactly which components are available — no system prompt needed.
Create .claude/settings.json in your project root:
{
"mcpServers": {
"mdx-ui": {
"command": "pnpm",
"args": ["dlx", "@ravikumarsurya/mdx-ui@latest", "mcp"]
}
}
}Replace pnpm dlx with npx or yarn dlx depending on your package manager.
Open the project in VS Code with Claude Code. The server starts automatically and gives Claude live access to your component registry. You can then ask Claude to generate MDX content — it will call list_components, convert_latex, and validate_mdx automatically before returning output.
See the MCP Server doc for the full list of tools and prompts.
Next Steps
On This Page