Mendix 11 introduced an exciting capability: building custom document editors directly in Studio Pro. In this tutorial, we'll build a Markdown editor extension from scratch using TypeScript and React.

Why Build Custom Document Editors?

With .NET extensions no longer actively developed, the new TypeScript/React-based extension system offers a modern approach to extending Studio Pro. Custom document editors allow you to:

  • Edit specialized file formats directly in Studio Pro
  • Integrate third-party libraries and tools
  • Create seamless workflows without leaving the IDE

Project Setup

Start by creating a new extension project using the npm scaffolding tool:

npm create mendix-extension

This sets up the basic structure with TypeScript and React support. For our Markdown editor, we'll use the MDX Editor - an open-source React library specifically designed for Markdown editing.

Project Structure

Your extension will have the following key files:

my-extension/
├── main/
│   └── index.ts          # Main entry point
├── editor/
│   └── editor.tsx        # React editor component
├── concepts/
│   └── markdown-type.ts  # Document type definition
└── manifest.json         # Extension configuration

Step 1: Register the Document Type

In main/index.ts, register your custom document type with the Studio Pro API:

import { registerMarkdownType } from './concepts/markdown-type';

// Register the custom Markdown document type
registerMarkdownType();

This blocks the default document type and registers your Markdown variant.

Step 2: Define Document Type Properties

Create concepts/markdown-type.ts to define the document type:

export function registerMarkdownType() {
  // Define icons as Base64 strings (keep them small for fast loading)
  const smallIcon = "data:image/png;base64,...";
  const largeIcon = "data:image/png;base64,...";
  
  // Define the storage pattern
  const storagePattern = {
    type: 'blob',
    contentType: 'text/markdown'
  };
  
  // Register the editor
  mx.editor.register({
    name: 'Markdown',
    icon: smallIcon,
    largeIcon: largeIcon,
    storagePattern: storagePattern,
    defaultContent: '', // Empty by default
    editorType: 'fullscreen' // Opens as a full-screen tab, not a popup
  });
}
Keep your icon files small (Base64-encoded) to ensure fast loading times in Studio Pro.

Step 3: Build the React Editor

Create editor/editor.tsx with the MDX Editor component:

import React, { useState, useEffect } from 'react';
import { MDXEditor } from '@mdxeditor/editor';
import '@mdxeditor/editor/style.css';

export function MarkdownEditorComponent() {
  const [content, setContent] = useState('');
  const [documentId, setDocumentId] = useState(null);
  
  // Load document data from Mendix
  useEffect(() => {
    async function loadDocument() {
      const doc = await mx.document.getById(documentId);
      const blobContent = await doc.getContent();
      setContent(blobContent);
    }
    
    if (documentId) {
      loadDocument();
    }
  }, [documentId]);
  
  // Save changes back to Mendix
  const handleChange = (newContent: string) => {
    setContent(newContent);
    
    // Update the blob document so it can be manually saved
    if (documentId) {
      mx.document.updateContent(documentId, newContent);
    }
  };
  
  return (
    <React.StrictMode>
      <MDXEditor 
        markdown={content}
        onChange={handleChange}
      />
    </React.StrictMode>
  );
}

Key Implementation Details

  1. Data Loading: Use mx.document.getById() to retrieve the document content from Mendix's blob storage
  2. Data Saving: Update the blob document with updateContent() when the editor changes
  3. React Strict Mode: Wrap your component for better development experience

Step 4: Configure the Manifest

In manifest.json, define your entry points:

{
  "name": "Markdown Editor",
  "version": "1.0.0",
  "entryPoints": {
    "main": "dist/main/index.js",
    "editor": "dist/editor/editor.js"
  }
}

Step 5: Handle CSS Styling

By default, extensions only support React CSS props. To use raw CSS files, you need to modify the build configuration.

Add CSS bundling plugins to your build configuration:

// vite.config.js or webpack.config.js
import { copyCSSPlugin } from 'vite-plugin-copy-css';
import { bundleCSSPlugin } from 'vite-plugin-bundle-css';

export default {
  plugins: [
    copyCSSPlugin(),
    bundleCSSPlugin()
  ]
}

This configuration:

  • Combines all CSS files in your project into a single style.css
  • Automatically injects it into the final build

Step 6: Build and Deploy

Build your extension:

npm run build

This compiles TypeScript, bundles React components, and combines all CSS files.

Step 7: Load in Studio Pro

  1. Copy your built extension to the Mendix project's userlib/extensions/ folder
  2. In Studio Pro, go to App > Synchronize App Directory
  3. Your extension will now be available

Using Your Extension

  1. In Studio Pro, right-click in the Project Explorer
  2. Select Add Document
  3. Choose Markdown from the document types
  4. Name it (e.g., "Project Plan")
  5. The Markdown editor opens in a full-screen tab
  6. Edit your content and save with Ctrl+S

What's Next?

Now that you've built a functional document editor extension, you're ready to explore more advanced topics. In the next post, we'll dive into styling your extension to match Studio Pro's design language and create a polished user experience.

Resources