GeniXCMS

GxEditor (Block Editor)

categoryHow To edit_calendar04 Apr 2026

GxEditor: The official GeniXCMS Block Editor

GxEditor is a modern, modular block-based editor bundled with GeniXCMS. It is inspired by Gutenberg and EditorJS, providing a seamless authoring experience while maintaining clean, structured HTML output.


1. Enabling GxEditor

To use GxEditor on a page or post, ensure it is selected as the active editor in Settings > System > Content Settings.

Theme Integration

In your theme, call the editor setup before rendering your textarea:

{Theme::editor('full', '400')|noescape}

<textarea name="content" class="editor">
    {$post->content|noescape}
</textarea>

2. Standard Blocks

GxEditor comes with a variety of built-in blocks:

  • Text: Paragraphs and headers (H1-H4).
  • Media: Images (integrated with elFinder) and Icons.
  • Layout: 2-Column Grids, 2x2 Grids, and Cards.
  • Dynamic: Recent Posts, Random Posts, and Table of Contents (TOC).
  • Structure: Tables, Lists (UL/OL), and Icon-marker Lists.
  • Advanced: Math Equations (KaTeX integration).

3. Advanced Block Features

Math Equations

GxEditor features a built-in Math Editor powered by KaTeX.

  • Toolbar Icon: Look for the Sigma-Root symbol (Σ√).
  • Interaction: Opens a modal where you can type LaTeX code with a live preview.
  • Editing: Double-clicking an existing math equation in the editor will re-open the editor modal for quick adjustments.

Professional Table Wizard

Legacy prompt-based table insertion has been replaced with a proper modal wizard.

  • Configuration: Set rows, columns, and header preference.
  • Themes: Toggle Bootstrap table-striped and table-bordered styles directly from the insertion modal.
  • Contextual Actions: Right-click (or use the toolbar dropdown in Classic Mode) to add/remove rows and columns dynamically.

3. Creating Custom Blocks

One of GxEditor's strongest features is its extensibility. You can register custom blocks from any module.

Step 1: Register the Block (PHP)

Use the GxEditor::registerBlock() method in your module's init() or constructor.

GxEditor::registerBlock('my_custom_block', [
    'label' => 'My Feature',
    'icon'  => 'bi bi-lightning',
    'desc'  => 'Add a lightning-fast feature block'
]);

Step 2: Implement Rendering (JavaScript)

The actual rendering and serialization of blocks are handled by JavaScript in the browser. You need to hook into the GxEditor Javascript object.

Create a file my-block-editor.js and enqueue it.

// Register the block in the JS layer
GxEditor.registerBlock('my_custom_block', {
    placeholder: 'Enter your lightning text...',
    // (Optional) Custom render logic
    render: function(state, block, content, wrapper) {
        content.classList.add('my-feature-rendered');
        content.contentEditable = 'true';
        content.innerHTML = block.content || 'Default text';
    }
});

Step 3: Handle Serialization (Parser)

If your block needs specialized shortcode or HTML output, you must ensure the parser knows how to handle it. By default, GxEditor treats custom blocks as simple text containers if no special logic is provided.


4. GxEditor Architecture

GxEditor is built as a set of modular JS components:

  • gxeditor.js: Core state management and block registration.
  • gxeditor-renderer.js: Converts block JSON into editable DOM elements.
  • gxeditor-parser.js: Converts DOM/JSON into Final HTML/Shortcodes and vice versa.
  • gxeditor-ui.js: Toolbars, pickers, and context menus.

The State Object

Each editor instance has a state object containing:

  • blocks: An array of block objects {id, type, content, ...}.
  • shell: The main DOM container.
  • textarea: The source textarea being synced.

5. Advanced: Nested Editors

GxEditor supports nesting editors within blocks (like in the Grid or Card blocks). This is achieved by creating a new GxEditor instance for a specific child element.

// Example: Creating a nested editor inside a block render function
var subTa = document.createElement('textarea');
subTa.className = 'editor gxb-nested-editor';
content.appendChild(subTa);

// Initialize after a tick
setTimeout(function() {
    window.initShell(subTa);
}, 0);

See Also