├── .eslintrc.cjs ├── .gitignore ├── .prettierignore ├── .prettierrc ├── .yarn └── install-state.gz ├── .yarnrc.yml ├── examples ├── app-router │ ├── .eslintrc.json │ ├── .gitignore │ ├── app │ │ ├── favicon.ico │ │ ├── globals.css │ │ ├── layout.tsx │ │ ├── loading.tsx │ │ └── page.tsx │ ├── examples │ │ └── highlight-example.mdx │ ├── next.config.js │ ├── package.json │ ├── postcss.config.js │ ├── public │ │ ├── next.svg │ │ └── vercel.svg │ ├── readme.md │ ├── tailwind.config.ts │ └── tsconfig.json └── pages-router │ ├── .eslintrc.json │ ├── .gitignore │ ├── examples │ └── highlight-example.mdx │ ├── next.config.js │ ├── package.json │ ├── pages │ ├── _app.tsx │ ├── _document.tsx │ └── index.tsx │ ├── postcss.config.js │ ├── public │ ├── favicon.ico │ ├── next.svg │ └── vercel.svg │ ├── readme.md │ ├── styles │ └── globals.css │ ├── tailwind.config.ts │ └── tsconfig.json ├── package.json ├── packages └── mdx │ ├── package.json │ ├── src │ ├── client │ │ ├── default.tsx │ │ ├── index.ts │ │ └── rsc.tsx │ ├── index.ts │ ├── lib │ │ └── syntaxHighlighting │ │ │ └── blade.js │ ├── plugins │ │ ├── index.ts │ │ └── rehype │ │ │ ├── index.ts │ │ │ └── rehypeSyntaxHighlighting.ts │ ├── server │ │ └── index.ts │ ├── styles │ │ └── prism.css │ └── types │ │ └── index.ts │ ├── tsconfig.build.json │ └── tsconfig.json ├── readme.md └── yarn.lock /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['@mintlify/eslint-config-typescript'], 3 | parserOptions: { 4 | tsconfigRootDir: __dirname, 5 | project: './tsconfig.json', 6 | }, 7 | ignorePatterns: ['.eslintrc.cjs', 'dist'], 8 | overrides: [ 9 | { 10 | files: ['*.js'], 11 | extends: ['plugin:@typescript-eslint/disable-type-checked'], 12 | }, 13 | ], 14 | }; 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # NPM 2 | node_modules/ 3 | .eslintcache 4 | yarn-error.log 5 | 6 | # Output 7 | dist/ 8 | 9 | # Misc 10 | .DS_Store 11 | 12 | # TypeScript 13 | *.tsbuildinfo 14 | 15 | # yarn 16 | .pnp.* 17 | .yarn/* 18 | !.yarn/patches 19 | !.yarn/plugins 20 | !.yarn/releases 21 | !.yarn/sdks 22 | !.yarn/versions -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | /dist 2 | /node_modules 3 | 4 | .next -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | "@mintlify/prettier-config/config.js" 2 | -------------------------------------------------------------------------------- /.yarn/install-state.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mintlify/mdx/eab9962762ce53f90d1f4d990e40bf45fb9e798f/.yarn/install-state.gz -------------------------------------------------------------------------------- /.yarnrc.yml: -------------------------------------------------------------------------------- 1 | nodeLinker: node-modules -------------------------------------------------------------------------------- /examples/app-router/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /examples/app-router/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | .yarn/install-state.gz 8 | 9 | # testing 10 | /coverage 11 | 12 | # next.js 13 | /.next/ 14 | /out/ 15 | 16 | # production 17 | /build 18 | 19 | # misc 20 | .DS_Store 21 | *.pem 22 | 23 | # debug 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # local env files 29 | .env*.local 30 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | -------------------------------------------------------------------------------- /examples/app-router/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mintlify/mdx/eab9962762ce53f90d1f4d990e40bf45fb9e798f/examples/app-router/app/favicon.ico -------------------------------------------------------------------------------- /examples/app-router/app/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | @layer base { 6 | :root { 7 | --primary-light: 85 215 153; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/app-router/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import '@mintlify/mdx/dist/styles.css'; 2 | import type { Metadata } from 'next'; 3 | 4 | import '@/app/globals.css'; 5 | 6 | export const metadata: Metadata = { 7 | title: 'Create Next App', 8 | description: 'Generated by create next app', 9 | }; 10 | 11 | export default function RootLayout({ children }: { children: React.ReactNode }) { 12 | return ( 13 | 14 | {children} 15 | 16 | ); 17 | } 18 | -------------------------------------------------------------------------------- /examples/app-router/app/loading.tsx: -------------------------------------------------------------------------------- 1 | export default function Loading() { 2 | return <>Loading...; 3 | } 4 | -------------------------------------------------------------------------------- /examples/app-router/app/page.tsx: -------------------------------------------------------------------------------- 1 | import { MDXRemote } from '@mintlify/mdx'; 2 | import { promises as fs } from 'fs'; 3 | 4 | export default async function Home() { 5 | const data = await fs.readFile(process.cwd() + '/examples/highlight-example.mdx', 'utf8'); 6 | 7 | return ( 8 |
9 | 10 |
11 | ); 12 | } 13 | -------------------------------------------------------------------------------- /examples/app-router/examples/highlight-example.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'Line Highlighting' 3 | description: 'Highlights specific lines and/or line ranges' 4 | --- 5 | 6 | This MDX file demonstrates syntax highlighting for various programming languages. 7 | 8 | ## JavaScript 9 | 10 | ```js index.js {2} 11 | console.log('Hello, world!'); 12 | function add(a, b) { 13 | return a + b; 14 | } 15 | 16 | function subtract(a, b) { 17 | return a - b; 18 | } 19 | ``` 20 | 21 | ## Python 22 | 23 | ```python index.py {1-2,4-5} 24 | def add(a, b): 25 | return a + b 26 | 27 | def subtract(a, b): 28 | return a - b 29 | ``` 30 | 31 | ## Java 32 | 33 | ```java {3} 34 | public class Main { 35 | public static void main(String[] args) { 36 | System.out.println("Hello, World!"); 37 | } 38 | } 39 | ``` 40 | 41 | ## C# 42 | 43 | ```csharp index.cs {1,3-4} 44 | public class Program 45 | { 46 | public static void Main(string[] args) 47 | { 48 | Console.WriteLine("Hello, World!"); 49 | } 50 | } 51 | ``` 52 | -------------------------------------------------------------------------------- /examples/app-router/next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = {}; 3 | 4 | module.exports = nextConfig; 5 | -------------------------------------------------------------------------------- /examples/app-router/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "app-router", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint" 10 | }, 11 | "dependencies": { 12 | "@mintlify/mdx": "workspace:^", 13 | "next": "14.0.4", 14 | "react": "^18", 15 | "react-dom": "^18" 16 | }, 17 | "devDependencies": { 18 | "@tailwindcss/typography": "^0.5.10", 19 | "@types/node": "^20", 20 | "@types/react": "^18", 21 | "@types/react-dom": "^18", 22 | "autoprefixer": "^10.0.1", 23 | "eslint": "^8", 24 | "eslint-config-next": "14.0.4", 25 | "postcss": "^8", 26 | "tailwindcss": "^3.3.0", 27 | "typescript": "^5" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /examples/app-router/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /examples/app-router/public/next.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/app-router/public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/app-router/readme.md: -------------------------------------------------------------------------------- 1 | ## Getting Started 2 | 3 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app) and it uses the [App Router](https://nextjs.org/docs/app). It also uses [Tailwind CSS](https://tailwindcss.com/) for styling. 4 | 5 | You can check out the code at [https://github.com/mintlify/mdx/blob/main/examples/app-router/app/page.tsx](https://github.com/mintlify/mdx/blob/main/examples/app-router/app/page.tsx) to understand how to parse your markdown using [@mintlify/mdx](https://www.npmjs.com/package/@mintlify/mdx). 6 | 7 | ## Demo 8 | 9 | You can check out the demo of [this page](https://github.com/mintlify/mdx/blob/main/examples/app-router/app/page.tsx) at [https://mdx-app-router.vercel.app](https://mdx-app-router.vercel.app). 10 | 11 | ## How to use 12 | 13 | 1. Use the `MDXRemote` component directly inside your async React Server Component. 14 | 15 | ```tsx 16 | import { MDXRemote } from '@mintlify/mdx'; 17 | 18 | export default async function Home() { 19 | const source: `--- 20 | title: Title 21 | --- 22 | 23 | ## Markdown H2 24 | `; 25 | 26 | return ( 27 |
28 | 29 |
30 | ); 31 | } 32 | ``` 33 | 34 | 2. Import `@mintlify/mdx/dist/styles.css` inside your `layout.tsx` file. This file contains the styles for the code syntax highlighting. 35 | 36 | ```tsx 37 | import '@mintlify/mdx/dist/styles.css'; 38 | import type { Metadata } from 'next'; 39 | 40 | export const metadata: Metadata = { 41 | title: 'Create Next App', 42 | description: 'Generated by create next app', 43 | }; 44 | 45 | export default function RootLayout({ children }: { children: React.ReactNode }) { 46 | return ( 47 | 48 | {children} 49 | 50 | ); 51 | } 52 | ``` 53 | -------------------------------------------------------------------------------- /examples/app-router/tailwind.config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from 'tailwindcss'; 2 | 3 | const config: Config = { 4 | content: [ 5 | './pages/**/*.{js,ts,jsx,tsx,mdx}', 6 | './components/**/*.{js,ts,jsx,tsx,mdx}', 7 | './app/**/*.{js,ts,jsx,tsx,mdx}', 8 | ], 9 | theme: { 10 | extend: { 11 | backgroundImage: { 12 | 'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))', 13 | 'gradient-conic': 'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))', 14 | }, 15 | }, 16 | }, 17 | plugins: [require('@tailwindcss/typography')], 18 | }; 19 | export default config; 20 | -------------------------------------------------------------------------------- /examples/app-router/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "strict": true, 8 | "noEmit": true, 9 | "esModuleInterop": true, 10 | "module": "esnext", 11 | "moduleResolution": "bundler", 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "jsx": "preserve", 15 | "incremental": true, 16 | "plugins": [ 17 | { 18 | "name": "next" 19 | } 20 | ], 21 | "paths": { 22 | "@/*": ["./*"] 23 | } 24 | }, 25 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 26 | "exclude": ["node_modules"] 27 | } 28 | -------------------------------------------------------------------------------- /examples/pages-router/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /examples/pages-router/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | .yarn/install-state.gz 8 | 9 | # testing 10 | /coverage 11 | 12 | # next.js 13 | /.next/ 14 | /out/ 15 | 16 | # production 17 | /build 18 | 19 | # misc 20 | .DS_Store 21 | *.pem 22 | 23 | # debug 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # local env files 29 | .env*.local 30 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | -------------------------------------------------------------------------------- /examples/pages-router/examples/highlight-example.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'Line Highlighting' 3 | description: 'Highlights specific lines and/or line ranges' 4 | --- 5 | 6 | This MDX file demonstrates syntax highlighting for various programming languages. 7 | 8 | ## JavaScript 9 | 10 | ```js index.js {2} 11 | console.log('Hello, world!'); 12 | function add(a, b) { 13 | return a + b; 14 | } 15 | 16 | function subtract(a, b) { 17 | return a - b; 18 | } 19 | ``` 20 | 21 | ## Python 22 | 23 | ```python index.py {1-2,4-5} 24 | def add(a, b): 25 | return a + b 26 | 27 | def subtract(a, b): 28 | return a - b 29 | ``` 30 | 31 | ## Java 32 | 33 | ```java {3} 34 | public class Main { 35 | public static void main(String[] args) { 36 | System.out.println("Hello, World!"); 37 | } 38 | } 39 | ``` 40 | 41 | ## C# 42 | 43 | ```csharp index.cs {1,3-4} 44 | public class Program 45 | { 46 | public static void Main(string[] args) 47 | { 48 | Console.WriteLine("Hello, World!"); 49 | } 50 | } 51 | ``` 52 | -------------------------------------------------------------------------------- /examples/pages-router/next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = {}; 3 | 4 | module.exports = nextConfig; 5 | -------------------------------------------------------------------------------- /examples/pages-router/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pages-router", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint" 10 | }, 11 | "dependencies": { 12 | "@mintlify/mdx": "workspace:^", 13 | "next": "14.0.4", 14 | "react": "^18", 15 | "react-dom": "^18" 16 | }, 17 | "devDependencies": { 18 | "@tailwindcss/typography": "^0.5.10", 19 | "@types/node": "^20", 20 | "@types/react": "^18", 21 | "@types/react-dom": "^18", 22 | "autoprefixer": "^10.0.1", 23 | "eslint": "^8", 24 | "eslint-config-next": "14.0.4", 25 | "postcss": "^8", 26 | "tailwindcss": "^3.3.0", 27 | "typescript": "^5" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /examples/pages-router/pages/_app.tsx: -------------------------------------------------------------------------------- 1 | import '@mintlify/mdx/dist/styles.css'; 2 | import { AppProps } from 'next/app'; 3 | 4 | import '@/styles/globals.css'; 5 | 6 | export default function App({ Component, pageProps }: AppProps) { 7 | return ; 8 | } 9 | -------------------------------------------------------------------------------- /examples/pages-router/pages/_document.tsx: -------------------------------------------------------------------------------- 1 | import { Html, Head, Main, NextScript } from 'next/document'; 2 | 3 | export default function Document() { 4 | return ( 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /examples/pages-router/pages/index.tsx: -------------------------------------------------------------------------------- 1 | import { MDXClient, serialize, SerializeResult } from '@mintlify/mdx'; 2 | import { promises as fs } from 'fs'; 3 | import type { GetStaticProps, InferGetStaticPropsType } from 'next'; 4 | 5 | export const getStaticProps = (async () => { 6 | const data = await fs.readFile(process.cwd() + '/examples/highlight-example.mdx', 'utf8'); 7 | 8 | const mdxSource = await serialize({ source: data }); 9 | if ('error' in mdxSource) { 10 | throw mdxSource.error; 11 | } 12 | 13 | return { props: { mdxSource } }; 14 | }) satisfies GetStaticProps<{ 15 | mdxSource: Omit; 16 | }>; 17 | 18 | export default function Home({ mdxSource }: InferGetStaticPropsType) { 19 | return ( 20 |
21 |

{String(mdxSource.frontmatter.title)}

22 | 23 | 24 |
25 | ); 26 | } 27 | -------------------------------------------------------------------------------- /examples/pages-router/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /examples/pages-router/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mintlify/mdx/eab9962762ce53f90d1f4d990e40bf45fb9e798f/examples/pages-router/public/favicon.ico -------------------------------------------------------------------------------- /examples/pages-router/public/next.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/pages-router/public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/pages-router/readme.md: -------------------------------------------------------------------------------- 1 | ## Getting Started 2 | 3 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app) and it uses the [Pages Router](https://nextjs.org/docs/pages). It also uses [Tailwind CSS](https://tailwindcss.com/) for styling. 4 | 5 | You can check out the code at [https://github.com/mintlify/mdx/blob/main/examples/pages-router/pages/index.tsx](https://github.com/mintlify/mdx/blob/main/examples/pages-router/pages/index.tsx) to understand how to parse your markdown using [@mintlify/mdx](https://www.npmjs.com/package/@mintlify/mdx). 6 | 7 | ## Demo 8 | 9 | You can check out the demo of [this page](https://github.com/mintlify/mdx/blob/main/examples/pages-router/pages/index.tsx) at [https://mdx-pages-router.vercel.app](https://mdx-pages-router.vercel.app). 10 | 11 | ## How to use 12 | 13 | 1. Call the `serialize` function inside `getStaticProps` and return the `mdxSource` object. 14 | 15 | ```tsx 16 | export const getStaticProps = (async () => { 17 | const mdxSource = await serialize({ 18 | source: '## Markdown H2', 19 | }); 20 | 21 | if ('error' in mdxSource) { 22 | // handle error case 23 | } 24 | 25 | return { props: { mdxSource } }; 26 | }) satisfies GetStaticProps<{ 27 | mdxSource: SerializeSuccess; 28 | }>; 29 | ``` 30 | 31 | 2. Pass the `mdxSource` object as props inside the `MDXComponent`. 32 | 33 | ```tsx 34 | export default function Page({ mdxSource }: InferGetStaticPropsType) { 35 | return ; 36 | } 37 | ``` 38 | 39 | 3. Import `@mintlify/mdx/dist/styles.css` inside your `_app.tsx` file. This file contains the styles for the code syntax highlighting. 40 | 41 | ```tsx 42 | import '@mintlify/mdx/dist/styles.css'; 43 | import { AppProps } from 'next/app'; 44 | 45 | export default function App({ Component, pageProps }: AppProps) { 46 | return ; 47 | } 48 | ``` 49 | -------------------------------------------------------------------------------- /examples/pages-router/styles/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | @layer base { 6 | :root { 7 | --primary-light: 85 215 153; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/pages-router/tailwind.config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from 'tailwindcss'; 2 | 3 | const config: Config = { 4 | content: [ 5 | './pages/**/*.{js,ts,jsx,tsx,mdx}', 6 | './components/**/*.{js,ts,jsx,tsx,mdx}', 7 | './app/**/*.{js,ts,jsx,tsx,mdx}', 8 | ], 9 | theme: { 10 | extend: { 11 | backgroundImage: { 12 | 'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))', 13 | 'gradient-conic': 'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))', 14 | }, 15 | }, 16 | }, 17 | plugins: [require('@tailwindcss/typography')], 18 | }; 19 | export default config; 20 | -------------------------------------------------------------------------------- /examples/pages-router/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "strict": true, 8 | "noEmit": true, 9 | "esModuleInterop": true, 10 | "module": "esnext", 11 | "moduleResolution": "bundler", 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "jsx": "preserve", 15 | "incremental": true, 16 | "paths": { 17 | "@/*": ["./*"] 18 | } 19 | }, 20 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], 21 | "exclude": ["node_modules"] 22 | } 23 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mdx", 3 | "private": true, 4 | "scripts": { 5 | "build": "yarn workspaces foreach --topological-dev -Av run build" 6 | }, 7 | "workspaces": [ 8 | "packages/*", 9 | "examples/*" 10 | ], 11 | "packageManager": "yarn@4.3.1" 12 | } 13 | -------------------------------------------------------------------------------- /packages/mdx/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@mintlify/mdx", 3 | "version": "1.0.1", 4 | "description": "Markdown parser from Mintlify", 5 | "main": "./dist/index.js", 6 | "types": "./dist/index.d.ts", 7 | "sideEffects": false, 8 | "files": [ 9 | "dist" 10 | ], 11 | "type": "module", 12 | "publishConfig": { 13 | "access": "public", 14 | "registry": "https://registry.npmjs.org/" 15 | }, 16 | "repository": { 17 | "type": "git", 18 | "url": "https://github.com/mintlify/mdx.git" 19 | }, 20 | "scripts": { 21 | "prepare": "npm run build", 22 | "build": "tsc --project tsconfig.build.json && yarn copy:css", 23 | "clean:build": "rimraf dist", 24 | "clean:all": "rimraf node_modules .eslintcache && yarn clean:build", 25 | "watch": "tsc --watch", 26 | "type": "tsc --noEmit", 27 | "lint": "eslint . --cache", 28 | "format": "prettier . --write", 29 | "format:check": "prettier . --check", 30 | "copy:css": "cp -r ./src/styles/prism.css ./dist/styles.css" 31 | }, 32 | "author": "Mintlify, Inc.", 33 | "license": "MIT", 34 | "devDependencies": { 35 | "@mintlify/eslint-config": "^1.0.4", 36 | "@mintlify/eslint-config-typescript": "^1.0.9", 37 | "@mintlify/prettier-config": "^1.0.1", 38 | "@mintlify/ts-config": "^2.0.2", 39 | "@trivago/prettier-plugin-sort-imports": "^4.3.0", 40 | "@tsconfig/recommended": "1.x", 41 | "@types/react": "^18.3.11", 42 | "@types/react-dom": "^18.3.0", 43 | "@typescript-eslint/eslint-plugin": "6.x", 44 | "@typescript-eslint/parser": "6.x", 45 | "eslint": "8.x", 46 | "eslint-config-prettier": "8.x", 47 | "eslint-plugin-unused-imports": "^3.x", 48 | "prettier": "^3.1.1", 49 | "prettier-plugin-tailwindcss": "^0.6.8", 50 | "react": "^18.3.1", 51 | "react-dom": "^18.3.1", 52 | "rimraf": "^5.0.1", 53 | "typescript": "^5.7.2" 54 | }, 55 | "peerDependencies": { 56 | "react": "^18.3.1", 57 | "react-dom": "^18.3.1" 58 | }, 59 | "dependencies": { 60 | "@types/hast": "^3.0.4", 61 | "@types/unist": "^3.0.3", 62 | "hast-util-to-string": "^3.0.1", 63 | "next-mdx-remote-client": "^1.0.3", 64 | "refractor": "^4.8.1", 65 | "rehype-katex": "^7.0.1", 66 | "remark-gfm": "^4.0.0", 67 | "remark-math": "^6.0.0", 68 | "remark-smartypants": "^3.0.2", 69 | "unified": "^11.0.0", 70 | "unist-util-visit": "^5.0.0" 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /packages/mdx/src/client/default.tsx: -------------------------------------------------------------------------------- 1 | export { MDXClient } from 'next-mdx-remote-client/csr'; 2 | -------------------------------------------------------------------------------- /packages/mdx/src/client/index.ts: -------------------------------------------------------------------------------- 1 | export * from './default.js'; 2 | export * from './rsc.js'; 3 | -------------------------------------------------------------------------------- /packages/mdx/src/client/rsc.tsx: -------------------------------------------------------------------------------- 1 | import { MDXRemote as BaseMDXRemote, MDXComponents } from 'next-mdx-remote-client/rsc'; 2 | import { SerializeOptions } from 'next-mdx-remote-client/serialize'; 3 | import rehypeKatex from 'rehype-katex'; 4 | import remarkGfm from 'remark-gfm'; 5 | import remarkMath from 'remark-math'; 6 | import remarkSmartypants from 'remark-smartypants'; 7 | 8 | import { rehypeSyntaxHighlighting } from '../plugins/index.js'; 9 | 10 | export async function MDXRemote({ 11 | source, 12 | mdxOptions, 13 | scope, 14 | components, 15 | parseFrontmatter, 16 | }: { 17 | source: string; 18 | mdxOptions?: SerializeOptions['mdxOptions']; 19 | scope?: SerializeOptions['scope']; 20 | components?: MDXComponents; 21 | parseFrontmatter?: SerializeOptions['parseFrontmatter']; 22 | }) { 23 | return ( 24 | // @ts-expect-error Server Component 25 | 47 | ); 48 | } 49 | -------------------------------------------------------------------------------- /packages/mdx/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './client/index.js'; 2 | export * from './server/index.js'; 3 | export * from './types/index.js'; 4 | -------------------------------------------------------------------------------- /packages/mdx/src/lib/syntaxHighlighting/blade.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Sources: 3 | * 1. https://github.com/PrismJS/prism/blob/master/components/prism-php.js 4 | * 2. https://github.com/Medalink/laravel-blade/blob/main/Syntaxes/Blade.sublime-syntax 5 | * 3. https://github.com/miken32/highlightjs-blade/blob/main/src/languages/blade.js 6 | */ 7 | import refractorMarkup from 'refractor/lang/markup.js'; 8 | import refractorPhp from 'refractor/lang/php.js'; 9 | 10 | blade.displayName = 'blade'; 11 | 12 | export default function blade(Prism) { 13 | Prism.register(refractorMarkup); 14 | Prism.register(refractorPhp); 15 | 16 | (function (Prism) { 17 | Prism.languages.blade = { 18 | comment: /{{--([\s\S]*?)--}}/, 19 | 20 | directive: { 21 | pattern: /@\w+(?:::\w+)?(?:\s*\([\s\S]*?\))?/, 22 | inside: { 23 | keyword: /@\w+/, 24 | function: /[:]\w+/, 25 | punctuation: /[():]/, 26 | }, 27 | }, 28 | 29 | echo: { 30 | pattern: /\{{2,3}[\s\S]*?\}{2,3}/, 31 | inside: { 32 | delimiter: /^\{{2,3}|\}{2,3}$/, 33 | php: { 34 | pattern: /[\s\S]+/, 35 | inside: Prism.languages.php, 36 | }, 37 | }, 38 | }, 39 | 40 | php: { 41 | pattern: /(?:\@php[\s\S]*?\@endphp|\<\?php[\s\S]*?\?\>)/, 42 | inside: { 43 | delimiter: { 44 | pattern: /^\@php|\@endphp|\<\?php|\?\>$/, 45 | alias: 'important', 46 | }, 47 | php: { 48 | pattern: /[\s\S]+/, 49 | inside: Prism.languages.php, 50 | }, 51 | }, 52 | }, 53 | 54 | markup: { 55 | pattern: /<[^?]\/?(.*?)>/, 56 | inside: Prism.languages.markup, 57 | }, 58 | 59 | keyword: 60 | /\b(?:@if|@else|@elseif|@endif|@foreach|@endforeach|@for|@endfor|@while|@endwhile|@unless|@endunless|@isset|@endisset|@empty|@endempty|@switch|@case|@break|@default|@endswitch|@include|@extends|@section|@endsection|@yield|@stack|@push|@endpush|@auth|@guest|@endauth|@endguest)\b/, 61 | 62 | variable: /\$\w+/, 63 | 64 | operator: /=>|->|\|\||&&|!=|==|<=|>=|[+\-*\/%<>]=?|\?:/, 65 | 66 | punctuation: /[\[\](){}:;,]/, 67 | }; 68 | })(Prism); 69 | } 70 | -------------------------------------------------------------------------------- /packages/mdx/src/plugins/index.ts: -------------------------------------------------------------------------------- 1 | export * from './rehype/index.js'; 2 | -------------------------------------------------------------------------------- /packages/mdx/src/plugins/rehype/index.ts: -------------------------------------------------------------------------------- 1 | export * from './rehypeSyntaxHighlighting.js'; 2 | -------------------------------------------------------------------------------- /packages/mdx/src/plugins/rehype/rehypeSyntaxHighlighting.ts: -------------------------------------------------------------------------------- 1 | import { Element, ElementContent, Root } from 'hast'; 2 | import { toString } from 'hast-util-to-string'; 3 | import { refractor } from 'refractor/lib/all.js'; 4 | import { Plugin } from 'unified'; 5 | import { visit } from 'unist-util-visit'; 6 | 7 | import blade from '../../lib/syntaxHighlighting/blade.js'; 8 | 9 | refractor.register(blade); 10 | 11 | export type RehypeSyntaxHighlightingOptions = { 12 | ignoreMissing?: boolean; 13 | alias?: Record; 14 | }; 15 | 16 | const lineHighlightPattern = /\{(.*?)\}/; 17 | 18 | function classNameOrEmptyArray(element: Element): string[] { 19 | const className = element.properties.className; 20 | if (Array.isArray(className) && className.every((el) => typeof el === 'string')) return className; 21 | return []; 22 | } 23 | 24 | export const rehypeSyntaxHighlighting: Plugin<[RehypeSyntaxHighlightingOptions?], Root, Root> = ( 25 | options = {} 26 | ) => { 27 | if (options.alias) { 28 | refractor.alias(options.alias); 29 | } 30 | 31 | return (tree) => { 32 | visit(tree, 'element', (node, _index, parent) => { 33 | if ( 34 | !parent || 35 | parent.type !== 'element' || 36 | parent.tagName !== 'pre' || 37 | node.tagName !== 'code' 38 | ) { 39 | return; 40 | } 41 | 42 | const lang = getLanguage(node) || 'plaintext'; 43 | 44 | try { 45 | parent.properties.className = classNameOrEmptyArray(parent).concat('language-' + lang); 46 | const code = toString(node); 47 | const lines = code.split('\n'); 48 | const linesToHighlight = getLinesToHighlight(node, lines.length); 49 | 50 | const nodes = lines.reduce((acc, line, index) => { 51 | const isNotEmptyLine = line.trim() !== ''; 52 | // Line numbers start from 1 53 | const isHighlighted = linesToHighlight.includes(index + 1); 54 | 55 | if (isNotEmptyLine) { 56 | const node: Element = { 57 | type: 'element', 58 | tagName: 'span', 59 | properties: { 60 | className: [isHighlighted ? 'line-highlight' : ''], 61 | }, 62 | children: refractor.highlight(line, lang).children as ElementContent[], 63 | }; 64 | acc.push(node); 65 | } else { 66 | acc.push({ type: 'text', value: line }); 67 | } 68 | 69 | if (index < lines.length - 1) { 70 | acc.push({ type: 'text', value: '\n' }); 71 | } 72 | return acc; 73 | }, []); 74 | 75 | if (node.data?.meta) { 76 | // remove line highlight meta 77 | node.data.meta = (node.data.meta as string).replace(lineHighlightPattern, '').trim(); 78 | } 79 | 80 | node.children = nodes; 81 | } catch (err) { 82 | if (options.ignoreMissing && /Unknown language/.test((err as Error).message)) { 83 | return; 84 | } 85 | throw err; 86 | } 87 | }); 88 | }; 89 | }; 90 | 91 | function getLanguage(node: Element): string | null { 92 | const className = classNameOrEmptyArray(node); 93 | 94 | for (const classListItem of className) { 95 | if (classListItem.slice(0, 9) === 'language-') { 96 | const lang = classListItem.slice(9).toLowerCase(); 97 | 98 | if (refractor.registered(lang)) { 99 | return lang; 100 | } 101 | return null; 102 | } 103 | } 104 | 105 | return null; 106 | } 107 | 108 | function getLinesToHighlight(node: Element, maxLines: number): number[] { 109 | const meta = 110 | typeof node.data?.meta === 'string' 111 | ? node.data.meta 112 | : classNameOrEmptyArray(node).reduce((acc, item) => acc + ' ' + item, ''); 113 | if (!meta) return []; 114 | 115 | const content = meta.match(lineHighlightPattern)?.[1]?.trim(); 116 | if (!content) return []; 117 | 118 | const lineNumbers = new Set(); 119 | 120 | content.split(',').forEach((part) => { 121 | const [start, end] = part.split('-').map((num) => { 122 | const trimmed = num.trim(); 123 | if (!/^\d+$/.test(trimmed)) return undefined; 124 | const parsed = parseInt(trimmed, 10); 125 | return parsed > maxLines ? maxLines : parsed; 126 | }); 127 | 128 | if (!start) return; 129 | const endLine = end ?? start; 130 | 131 | if (endLine < start) return; 132 | const max = Math.min(endLine, maxLines); 133 | for (let i = start; i <= max; i++) { 134 | lineNumbers.add(i); 135 | } 136 | }); 137 | 138 | return Array.from(lineNumbers).sort((a, b) => a - b); 139 | } 140 | -------------------------------------------------------------------------------- /packages/mdx/src/server/index.ts: -------------------------------------------------------------------------------- 1 | import { serialize as baseSerialize } from 'next-mdx-remote-client/serialize'; 2 | import rehypeKatex from 'rehype-katex'; 3 | import remarkGfm from 'remark-gfm'; 4 | import remarkMath from 'remark-math'; 5 | import remarkSmartypants from 'remark-smartypants'; 6 | 7 | import { rehypeSyntaxHighlighting } from '../plugins/index.js'; 8 | import type { SerializeOptions } from '../types/index.js'; 9 | 10 | export const serialize = async ({ 11 | source, 12 | mdxOptions, 13 | scope, 14 | parseFrontmatter = true, 15 | }: { 16 | source: string; 17 | mdxOptions?: SerializeOptions['mdxOptions']; 18 | scope?: SerializeOptions['scope']; 19 | parseFrontmatter?: SerializeOptions['parseFrontmatter']; 20 | }) => { 21 | try { 22 | return await baseSerialize({ 23 | source, 24 | options: { 25 | mdxOptions: { 26 | ...mdxOptions, 27 | remarkPlugins: [ 28 | remarkGfm, 29 | remarkSmartypants, 30 | remarkMath, 31 | ...(mdxOptions?.remarkPlugins || []), 32 | ], 33 | rehypePlugins: [ 34 | rehypeKatex, 35 | [ 36 | rehypeSyntaxHighlighting, 37 | { 38 | ignoreMissing: true, 39 | }, 40 | ], 41 | ...(mdxOptions?.rehypePlugins || []), 42 | ], 43 | format: mdxOptions?.format || 'mdx', 44 | }, 45 | scope, 46 | parseFrontmatter, 47 | }, 48 | }); 49 | } catch (error) { 50 | console.error(`Error occurred while serializing MDX: ${error}`); 51 | 52 | throw error; 53 | } 54 | }; 55 | -------------------------------------------------------------------------------- /packages/mdx/src/styles/prism.css: -------------------------------------------------------------------------------- 1 | /********************************************************* 2 | * Tokens 3 | */ 4 | .namespace { 5 | opacity: 0.7; 6 | } 7 | 8 | .token.doctype .token.doctype-tag { 9 | color: #569cd6; 10 | } 11 | 12 | .token.doctype .token.name { 13 | color: #9cdcfe; 14 | } 15 | 16 | .token.comment, 17 | .token.prolog { 18 | color: #6a9955; 19 | } 20 | 21 | .token.punctuation, 22 | .language-html .language-css .token.punctuation, 23 | .language-html .language-javascript .token.punctuation { 24 | color: #d4d4d4; 25 | } 26 | 27 | .token.property, 28 | .token.tag, 29 | .token.boolean, 30 | .token.number, 31 | .token.constant, 32 | .token.symbol, 33 | .token.inserted, 34 | .token.unit { 35 | color: #b5cea8; 36 | } 37 | 38 | .token.selector, 39 | .token.attr-name, 40 | .token.string, 41 | .token.char, 42 | .token.builtin, 43 | .token.deleted { 44 | color: #ce9178; 45 | } 46 | 47 | .token.plain-text { 48 | color: #fafafa; 49 | } 50 | 51 | .language-css .token.string.url { 52 | text-decoration: underline; 53 | } 54 | 55 | .token.operator, 56 | .token.entity { 57 | color: #d4d4d4; 58 | } 59 | 60 | .token.operator.arrow { 61 | color: #569cd6; 62 | } 63 | 64 | .token.atrule { 65 | color: #ce9178; 66 | } 67 | 68 | .token.atrule .token.rule { 69 | color: #c586c0; 70 | } 71 | 72 | .token.atrule .token.url { 73 | color: #9cdcfe; 74 | } 75 | 76 | .token.atrule .token.url .token.function { 77 | color: #dcdcaa; 78 | } 79 | 80 | .token.atrule .token.url .token.punctuation { 81 | color: #d4d4d4; 82 | } 83 | 84 | .token.keyword { 85 | color: #569cd6; 86 | } 87 | 88 | .token.keyword.module, 89 | .token.keyword.control-flow { 90 | color: #c586c0; 91 | } 92 | 93 | .token.function, 94 | .token.function .token.maybe-class-name { 95 | color: #dcdcaa; 96 | } 97 | 98 | .token.regex { 99 | color: #d16969; 100 | } 101 | 102 | .token.important { 103 | color: #569cd6; 104 | } 105 | 106 | .token.italic { 107 | font-style: italic; 108 | } 109 | 110 | .token.constant { 111 | color: #9cdcfe; 112 | } 113 | 114 | .token.class-name, 115 | .token.maybe-class-name { 116 | color: #4ec9b0; 117 | } 118 | 119 | .token.console { 120 | color: #9cdcfe; 121 | } 122 | 123 | .token.parameter { 124 | color: #9cdcfe; 125 | } 126 | 127 | .token.interpolation { 128 | color: #9cdcfe; 129 | } 130 | 131 | .token.punctuation.interpolation-punctuation { 132 | color: #569cd6; 133 | } 134 | 135 | .token.boolean { 136 | color: #569cd6; 137 | } 138 | 139 | .token.property, 140 | .token.variable, 141 | .token.imports .token.maybe-class-name, 142 | .token.exports .token.maybe-class-name { 143 | color: #9cdcfe; 144 | } 145 | 146 | .token.selector { 147 | color: #d7ba7d; 148 | } 149 | 150 | .token.escape { 151 | color: #d7ba7d; 152 | } 153 | 154 | .token.tag { 155 | color: #569cd6; 156 | } 157 | 158 | .token.tag .token.punctuation { 159 | color: #808080; 160 | } 161 | 162 | .token.cdata { 163 | color: #808080; 164 | } 165 | 166 | .token.attr-name { 167 | color: #9cdcfe; 168 | } 169 | 170 | .token.attr-value, 171 | .token.attr-value .token.punctuation { 172 | color: #ce9178; 173 | } 174 | 175 | .token.attr-value .token.punctuation.attr-equals { 176 | color: #d4d4d4; 177 | } 178 | 179 | .token.entity { 180 | color: #569cd6; 181 | } 182 | 183 | .token.namespace { 184 | color: #4ec9b0; 185 | } 186 | 187 | .token.operator { 188 | color: #94a3b8; 189 | } 190 | 191 | /********************************************************* 192 | * Language Specific 193 | */ 194 | 195 | pre[class*='language-javascript'], 196 | code[class*='language-javascript'], 197 | pre[class*='language-jsx'], 198 | code[class*='language-jsx'], 199 | pre[class*='language-typescript'], 200 | code[class*='language-typescript'], 201 | pre[class*='language-tsx'], 202 | code[class*='language-tsx'] { 203 | color: #9cdcfe; 204 | } 205 | 206 | pre[class*='language-css'], 207 | code[class*='language-css'] { 208 | color: #ce9178; 209 | } 210 | 211 | pre[class*='language-html'], 212 | code[class*='language-html'] { 213 | color: #d4d4d4; 214 | } 215 | 216 | .language-regex .token.anchor { 217 | color: #dcdcaa; 218 | } 219 | 220 | .language-html .token.punctuation { 221 | color: #808080; 222 | } 223 | 224 | /* Used for API response */ 225 | span.language-json .token.punctuation { 226 | color: #4b5563; 227 | } 228 | 229 | :is(.dark span.language-json .token.punctuation) { 230 | color: #d1d5db; 231 | } 232 | 233 | span.language-json .token.property { 234 | color: #0284c7; 235 | } 236 | 237 | :is(.dark span.language-json .token.property) { 238 | color: 9cdcfe; 239 | } 240 | 241 | span.language-json .token.string { 242 | color: #d97706; 243 | } 244 | 245 | :is(.dark span.language-json .token.string) { 246 | color: #ce9178; 247 | } 248 | 249 | span.language-json .token.number { 250 | color: #16a34a; 251 | } 252 | 253 | :is(.dark span.language-json .token.number) { 254 | color: #b5cea8; 255 | } 256 | 257 | code.language-toml .table { 258 | display: inherit; 259 | } 260 | 261 | /********************************************************* 262 | * Line highlighting 263 | */ 264 | 265 | pre[class*='language-'] > code[class*='language-'] { 266 | position: relative; 267 | z-index: 1; 268 | } 269 | 270 | .line-highlight { 271 | background: rgb(var(--primary-light) / 0.2); 272 | width: 100%; 273 | display: inline-block; 274 | position: relative; 275 | z-index: 0; 276 | } 277 | 278 | .line-highlight::before, 279 | .line-highlight::after { 280 | content: ''; 281 | position: absolute; 282 | top: 0; 283 | bottom: 0; 284 | width: 1rem; 285 | background: rgb(var(--primary-light) / 0.2); 286 | } 287 | 288 | .line-highlight::before { 289 | left: -1rem; 290 | border-left: 4px solid rgb(var(--primary-light) / 1); 291 | } 292 | .line-highlight::after { 293 | right: -1rem; 294 | } 295 | 296 | pre[class^='language-diff-'] { 297 | display: flex; 298 | padding-left: 2.25rem; 299 | padding-right: 2.25rem; 300 | } 301 | 302 | pre[class^='language-diff-'] > code { 303 | flex: none; 304 | min-width: 100%; 305 | } 306 | 307 | pre[class^='language-diff-'] > code { 308 | flex: none; 309 | min-width: 100%; 310 | } 311 | 312 | /* Lines of code outside code blocks. Generated when you use single ticks: `example` */ 313 | :not(pre) > code { 314 | overflow-wrap: break-word; 315 | border-radius: 0.375rem; 316 | padding-left: 0.375rem; 317 | padding-right: 0.375rem; 318 | padding-top: 1px; 319 | padding-bottom: 1px; 320 | border-width: 1px; 321 | border-color: #e5e7eb; 322 | background-color: #f9fafb; 323 | } 324 | 325 | :is(.dark :not(pre) > code) { 326 | border-color: #1f2937; 327 | background-color: rgb(15, 15, 15); 328 | color: #f3f4f6; 329 | } 330 | 331 | :not(pre) > code:before, 332 | :not(pre) > code:after { 333 | content: none !important; 334 | } 335 | -------------------------------------------------------------------------------- /packages/mdx/src/types/index.ts: -------------------------------------------------------------------------------- 1 | import type { SerializeOptions, SerializeResult } from 'next-mdx-remote-client/serialize'; 2 | 3 | type SerializeSuccess = SerializeResult & { compiledSource: string }; 4 | 5 | export type { SerializeOptions, SerializeResult, SerializeSuccess }; 6 | -------------------------------------------------------------------------------- /packages/mdx/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "include": ["src/**/*.ts", "src/**/*.tsx"] 4 | } 5 | -------------------------------------------------------------------------------- /packages/mdx/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@mintlify/ts-config", 3 | "compilerOptions": { 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "jsx": "react-jsx", 6 | "target": "ES2021", 7 | "outDir": "dist", 8 | "declaration": true, 9 | "module": "Node16" 10 | }, 11 | "include": ["**/*.ts", "**/*.tsx"], 12 | "exclude": ["node_modules", "dist"] 13 | } 14 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | Mintlify Logo 8 | 9 |
10 |

11 |

12 | 13 | Mint 14 | 15 |

16 |

17 |

18 | 19 | Open source docs builder that's beautiful, fast, and easy to work with. 20 | 21 |

22 |

23 | 24 | ![contributions welcome](https://img.shields.io/badge/contributions-welcome-brightgreen?logo=github) [![Tweet](https://img.shields.io/twitter/url?url=https%3A%2F%2Fmintlify.com%2F)](https://twitter.com/intent/tweet?url=&text=Check%20out%20%40mintlify) 25 | 26 |

27 |
28 | 29 | # Mintlify's markdown parser 30 | 31 | **@mintlify/mdx** is a thin layer on top of [next-mdx-remote-client](https://github.com/ipikuka/next-mdx-remote-client) that provides a better developer experience for Next.js users by adding support for syntax highlighting. 32 | 33 | ## Installation 34 | 35 | ```bash 36 | # using npm 37 | npm i @mintlify/mdx 38 | 39 | # using yarn 40 | yarn add @mintlify/mdx 41 | 42 | # using pnpm 43 | pnpm add @mintlify/mdx 44 | ``` 45 | 46 | ## Examples 47 | 48 | ### Next.js pages router 49 | 50 | [You can check the example app here](https://github.com/mintlify/mdx/tree/main/examples/pages-router). 51 | 52 | 1. Call the `serialize` function inside `getStaticProps` and return the `mdxSource` object. 53 | 54 | ```tsx 55 | export const getStaticProps = (async () => { 56 | const mdxSource = await serialize({ 57 | source: '## Markdown H2', 58 | }); 59 | 60 | if ('error' in mdxSource) { 61 | // handle error case 62 | } 63 | 64 | return { props: { mdxSource } }; 65 | }) satisfies GetStaticProps<{ 66 | mdxSource: SerializeSuccess; 67 | }>; 68 | ``` 69 | 70 | 2. Pass the `mdxSource` object as props inside the `MDXComponent`. 71 | 72 | ```tsx 73 | export default function Page({ mdxSource }: InferGetStaticPropsType) { 74 | return ; 75 | } 76 | ``` 77 | 78 | 3. Import `@mintlify/mdx/dist/styles.css` inside your `_app.tsx` file. This file contains the styles for the code syntax highlighting. 79 | 80 | ```tsx 81 | import '@mintlify/mdx/dist/styles.css'; 82 | import { AppProps } from 'next/app'; 83 | 84 | export default function App({ Component, pageProps }: AppProps) { 85 | return ; 86 | } 87 | ``` 88 | 89 | ### Next.js app router 90 | 91 | [You can check the example app here](https://github.com/mintlify/mdx/tree/main/examples/app-router). 92 | 93 | 1. Use the `MDXRemote` component directly inside your async React Server Component. 94 | 95 | ```tsx 96 | import { MDXRemote } from '@mintlify/mdx'; 97 | 98 | export default async function Home() { 99 | const source: `--- 100 | title: Title 101 | --- 102 | 103 | ## Markdown H2 104 | `; 105 | 106 | return ( 107 |
108 | 109 |
110 | ); 111 | } 112 | ``` 113 | 114 | 2. Import `@mintlify/mdx/dist/styles.css` inside your `layout.tsx` file. This file contains the styles for the code syntax highlighting. 115 | 116 | ```tsx 117 | import '@mintlify/mdx/dist/styles.css'; 118 | import type { Metadata } from 'next'; 119 | 120 | export const metadata: Metadata = { 121 | title: 'Create Next App', 122 | description: 'Generated by create next app', 123 | }; 124 | 125 | export default function RootLayout({ children }: { children: React.ReactNode }) { 126 | return ( 127 | 128 | {children} 129 | 130 | ); 131 | } 132 | ``` 133 | 134 | ## APIs 135 | 136 | Similar to [next-mdx-remote-client](https://github.com/ipikuka/next-mdx-remote-client), this package exports the following APIs: 137 | 138 | - `serialize` - a function that compiles MDX source to SerializeResult. 139 | - `MDXClient` - a component that renders SerializeSuccess on the client. 140 | - `MDXRemote` - a component that both serializes and renders the source - should be used inside async React Server Component. 141 | 142 | ### serialize 143 | 144 | ```tsx 145 | import { serialize } from '@mintlify/mdx'; 146 | 147 | const mdxSource = await serialize({ 148 | source: '## Markdown H2', 149 | mdxOptions: { 150 | remarkPlugins: [ 151 | // Remark plugins 152 | ], 153 | rehypePlugins: [ 154 | // Rehype plugins 155 | ], 156 | }, 157 | }); 158 | ``` 159 | 160 | ### MDXClient 161 | 162 | ```tsx 163 | 'use client'; 164 | 165 | import { MDXClient } from '@mintlify/mdx'; 166 | 167 | ; 173 | ``` 174 | 175 | ### MDXRemote 176 | 177 | ```tsx 178 | import { MDXRemote } from '@mintlify/mdx'; 179 | 180 | ; 194 | ``` 195 | 196 |
197 |

198 | 199 | Built with ❤︎ by 200 | 201 | Mintlify 202 | 203 | 204 |

205 |
206 | --------------------------------------------------------------------------------