Getting started

You've decided to give MDXEditor a try? That's great, because it does not take a lot of effort. While powerful, the component needs little to boot. In this article, we will go through the necessary steps to reach the "Hello world" state.

Installation

To use MDXEditor in your project, install the @mdxeditor/editor NPM package in your React project:

npm install --save @mdxeditor/editor

Afterwards, you can import the React component and use it in your project. You need to include the CSS file as well, either as an import in React or in your project stylesheet. Follow the instructions below for your framework of choice.

import { MDXEditor } from '@mdxeditor/editor'
import '@mdxeditor/editor/style.css'

Next.js (App router)

MDXEditor does not support server rendering, so we need to ensure that the editor component is rendered only on client-side. To do so, we can use the dynamic utility with {ssr: false}.

NOTE: Make sure that the editor plugins are initialized client-side only, too. Using some plugins will cause hydration errors if imported during SSR.

'use client'
// InitializedMDXEditor.tsx
import type { ForwardedRef } from 'react'
import {
  headingsPlugin,
  listsPlugin,
  quotePlugin,
  thematicBreakPlugin,
  markdownShortcutPlugin,
  MDXEditor,
  type MDXEditorMethods,
  type MDXEditorProps
} from '@mdxeditor/editor'

// Only import this to the next file
export default function InitializedMDXEditor({
  editorRef,
  ...props
}: { editorRef: ForwardedRef<MDXEditorMethods> | null } & MDXEditorProps) {
  return (
    <MDXEditor
      plugins={[
        // Example Plugin Usage
        headingsPlugin(),
        listsPlugin(),
        quotePlugin(),
        thematicBreakPlugin(),
        markdownShortcutPlugin()
      ]}
      {...props}
      ref={editorRef}
    />
  )
}
'use client'
// ForwardRefEditor.tsx

// This is the only place InitializedMDXEditor is imported directly.
const Editor = dynamic(() => import('./InitializedMDXEditor'), {
  // Make sure we turn SSR off
  ssr: false
})

// This is what is imported by other components. Pre-initialized with plugins, and ready
// to accept other props, including a ref.
export const ForwardRefEditor = forwardRef<MDXEditorMethods, MDXEditorProps>((props, ref) => <Editor {...props} editorRef={ref} />)

// TS complains without the following line
ForwardRefEditor.displayName = 'ForwardRefEditor'

If you get stuck, check the MDX editor in Next.js GitHub sample repository for a working example.

Next.js (Pages router)

Next.js in pages mode seems to choke on the ESM format of the editor and one of its dependencies. To work around that, include the following packages in your transpilation list in next.config.js:

/** @type {import('next').NextConfig} */
const nextConfig = {
  transpilePackages: ['@mdxeditor/editor'],
  reactStrictMode: true,
  webpack: (config) => {
    // this will override the experiments
    config.experiments = { ...config.experiments, topLevelAwait: true }
    // this will just update topLevelAwait property of config.experiments
    // config.experiments.topLevelAwait = true
    return config
  }
}

module.exports = nextConfig

Check the MDX editor in Next.js (pages) GitHub sample repository for a working example for a working example.

Vite

MDXEditor "just works" in Vite, assuming that you use a recent version of it.

Here's a minimal example for App.tsx:

import '@mdxeditor/editor/style.css'

// importing the editor and the plugin from their full paths
import { MDXEditor } from '@mdxeditor/editor/MDXEditor'
import { headingsPlugin } from '@mdxeditor/editor/plugins/headings'

function App() {
  return <MDXEditor markdown="# Hello world" plugins={[headingsPlugin()]} />
}

export default App

If you get stuck, check the MDX editor in Vite GitHub sample repository for a working example.

Remix

The component works well with the ClientOnly remix utility. Check the working example in the MDX editor in the Remix GitHub sample repository.

Create React App

Here's a minimal example for App.tsx:

import '@mdxeditor/editor/style.css'
import { MDXEditor, headingsPlugin } from '@mdxeditor/editor'

function App() {
  return <MDXEditor markdown={'# Hello World'} plugins={[headingsPlugin()]} />
}

export default App

See the MDX editor in CRA GitHub sample repository for a working example.

Basic usage

The MDXEditor component accepts its initial value through the markdown property. Notice that the property works like the textarea defaultValue. To change the value dynamically, you should use the setMarkdown ref method.

To listen for changes of the value of the editor, use the onChange callback property. The event is triggered continuously as the user types, so you can use it to update your state.

Alternatively, to obtain the value of the editor, use the getMarkdown ref method.

// create a ref to the editor component
const ref = React.useRef<MDXEditorMethods>(null)
return (
  <>
    <button onClick={() => ref.current?.setMarkdown('new markdown')}>Set new markdown</button>
    <button onClick={() => console.log(ref.current?.getMarkdown())}>Get markdown</button>
    <MDXEditor ref={ref} markdown="hello world" onChange={console.log} />
  </>
)

If you want to insert markdown content after the editor is initialized, you can use insertMarkdown ref method to insert markdown content to the current cursor position of the active editor.

// create a ref to the editor component
const ref = React.useRef<MDXEditorMethods>(null)
return (
  <>
    <button onClick={() => ref.current?.insertMarkdown('new markdown to insert')}>Insert new markdown</button>
    <button onClick={() => console.log(ref.current?.getMarkdown())}>Get markdown</button>
    <MDXEditor ref={ref} markdown="hello world" onChange={console.log} />
  </>
)

Next steps

Hopefully, at this point, the editor component is installed and working in your setup, but it's not very useful. Depending on your use case, you will need some additional features. To ensure that the bundle size stays small, MDXEditor uses a plugin system. Below is an example of a few basic plugins being enabled for the editor.

import { MDXEditor, headingsPlugin, listsPlugin, quotePlugin, thematicBreakPlugin } from '@mdxeditor/editor'

function App() {
  return <MDXEditor markdown="# Hello world" plugins={[headingsPlugin(), listsPlugin(), quotePlugin(), thematicBreakPlugin()]} />
}

Follow the links in the sidebar to learn more about each respective capability and the way to enable it.