├── .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 |
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 |
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 |  [](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 |
206 |
--------------------------------------------------------------------------------