Install dependencies:
npm installRun the development server with component examples:
npm run devThis starts Ladle on http://localhost:61000 where you can browse and test the examples.
Build and development:
npm run build- Build the library with Vitenpm run dev- Start Ladle dev server (component explorer)npm start- Alias fornpm run dev
Code quality:
npm run typecheck- Run TypeScript type checkingnpm run lint- Lint source files with ESLint
Testing:
npm test- Run Vitest in watch modenpm run test:once- Run Vitest once (CI mode)
Tests are located in src/test/**/*.test.{ts,tsx}
MDXEditor is built on:
- React 18/19 with TypeScript
- Lexical - Facebook's extensible text editor framework
- Gurx - Reactive state management library
- MDAST - Markdown abstract syntax tree
The editor uses a plugin architecture with Gurx for state management. Each feature is implemented as a plugin that can:
- Register custom Lexical nodes
- Add markdown import/export visitors
- Provide toolbar UI components
- Manage feature-specific state
Plugin structure:
export const myPlugin = realmPlugin({
init: (realm, params) => {
/* register nodes, visitors, cells */
},
postInit: (realm, params) => {
/* access other plugins' state */
},
update: (realm, params) => {
/* handle prop updates */
}
})src/plugins/- Plugin implementations (headings, lists, table, image, codeblock, etc.)src/plugins/core/- Core plugin with fundamental functionalitysrc/plugins/toolbar/- Toolbar UI componentssrc/examples/- Ladle stories (component examples)src/jsx-editors/- Editors for JSX componentssrc/directive-editors/- Editors for directives (admonitions, etc.)src/styles/- CSS modules and themingsrc/utils/- Utility functionssrc/test/- Test files
The editor maintains bidirectional conversion between markdown and Lexical's internal state:
Import (Markdown → Lexical):
- Parses markdown to MDAST using micromark
- Converts MDAST nodes to Lexical nodes using the
MdastImportVisitorinterface
Export (Lexical → Markdown):
- Converts Lexical nodes to MDAST using
LexicalExportVisitorinterface - Serializes MDAST to markdown
Each plugin registers both import and export visitors for its node types.
- Use path alias
@/for imports fromsrc/directory - CSS Modules with camelCase class names
- Gurx exports suffixed with
$(e.g.,markdown$,applyBlockType$) - Lexical functions prefixed with
$for editor read/update cycles (e.g.,$isCodeBlockNode) - TypeScript strict mode enabled
- Create a new plugin in
src/plugins/your-feature/ - Implement custom Lexical nodes if needed
- Add MDAST import/export visitors for markdown conversion
- Add toolbar components if needed
- Create examples in
src/examples/ - Write tests in
src/test/ - Export the plugin from
src/index.ts
- Ensure
npm run typecheckandnpm run lintpass - Add tests for new features
- Update examples in
src/examples/to demonstrate the feature - Keep commits focused and well-described