How can I help you?
Integrate Code Mirror into Angular Rich Text Editor Component
11 Feb 202612 minutes to read
RichTextEditor offers a basic HTML source view through the view-source property. For enhanced source editing features such as syntax highlighting, CodeMirror can be integrated with the Rich Text Editor.
Prerequisites
Before proceeding, complete the base Rich Text Editor setup described in the Getting Started guide. The guide covers Angular CLI setup, package installation, CSS imports, module injection, and basic editor markup: Getting Started with Angular Rich Text Editor.
Key features
- Replace the RTE source textarea with a CodeMirror EditorView.
- Preserve editor undo/redo by inserting source changes via the RTE APIs.
- Support light/dark themes and minimize bundle size via dynamic imports.
Setup or Installation
Install the required code mirror packages using the following command:
npm install codemirror @codemirror/state @codemirror/view @codemirror/lang-html @codemirror/theme-one-darkConfigure CodeMirror for the rich text editor
Step 1: import packages and providers
- import CodeMirror modules in the component where the integration runs. Provide ToolbarService, HtmlEditorService, ImageService, etc., as needed.
Step 2: Configure actionComplete event
- Configure the actionComplete event to call
mirrorConversionmethod while toggling betweenSourceCodeandPreview.
public actionCompleteHandler(e: any): void {
if (e.targetItem && (e.targetItem === 'SourceCode' || e.targetItem === 'Preview')) {
this.mirrorConversion(e);
}
}- On
SourceCode: create a temporary element and append to the rich text editor root cause and call renderCodeMirror method to render the code mirror.
mirrorView = createElement('div', { className: 'rte-code-mirror', id: id, styles: 'display: none;' });
this.rteObj!.rootContainer.appendChild(mirrorView);
this.renderCodeMirror(mirrorView, editorVlaue === null ? '' : editorVlaue);- On
Preview: serialize the EditorView document (state.doc.toString()) and set it as the RTE value, call dataBind(), and restore RTE focus.
this.rteObj!.value = this.myCodeMirror!.state.doc.toString();
this.rteObj!.dataBind();
this.rteObj!.rootContainer.classList.remove('e-rte-code-mirror-enabled');
this.rteObj!.focusIn();Step 3: Configure renderCodeMirror method
- Create a reusable renderCodeMirror function that builds an EditorState with desired extensions: basicSetup, html(), EditorView.lineWrapping, and optional theme extension.
const state = EditorState.create({
doc: content,
extensions: [ basicSetup, html(), EditorView.lineWrapping ]
});- Store the EditorView instance on the component and reuse it on subsequent SourceCode activations to preserve state.
const state = EditorState.create({
doc: content,
extensions: extensions
});
this.myCodeMirror = new EditorView({
state,
parent: mirrorView
});Step 4: how to implement dark theme
- Detect application or Syncfusion dark mode classes (for example, document.body.classList.contains(‘e-dark-mode’)).
- When dark mode is active, include the oneDark extension (or alternative theme) in the CodeMirror extensions array when creating the EditorState.
- Toggle theme only when recreating or updating the EditorState; prefer reusing the same EditorView and apply theme changes via state replacement if necessary.
let extensions;
if (document.body.classList && document.body.classList.contains('tailwind3-dark') && document.body.classList.contains('e-dark-mode')) {
extensions = [ basicSetup, html(), EditorView.lineWrapping, oneDark ]
} else {
extensions = [ basicSetup, html(), EditorView.lineWrapping ]
}Example for Code Mirror integration
Below is the example integration of Code Mirror with the Angular Rich Text Editor.
import { RichTextEditorAllModule, ToolbarSettingsModel } from '@syncfusion/ej2-angular-richtexteditor';
import { Component, ViewChild, ViewEncapsulation } from '@angular/core';
import { ToolbarService, LinkService, ImageService, HtmlEditorService, RichTextEditorComponent,CountService} from '@syncfusion/ej2-angular-richtexteditor';
import { createElement } from '@syncfusion/ej2-base';
import { EditorState } from '@codemirror/state';
import { html } from '@codemirror/lang-html';
import { EditorView } from '@codemirror/view';
import { basicSetup } from "codemirror";
import { oneDark } from '@codemirror/theme-one-dark';
@Component({
imports: [
RichTextEditorAllModule
],
standalone: true,
selector: 'app-root',
styleUrl: 'app.css',
encapsulation: ViewEncapsulation.None,
template: `<ejs-richtexteditor #editor [value]="value" [toolbarSettings]='tools' [showCharCount]='true' (actionComplete)='actionCompleteHandler($event)'></ejs-richtexteditor>`,
providers: [ToolbarService, LinkService, ImageService, HtmlEditorService, CountService]
})
export class App {
@ViewChild('editor')
public rteObj?: RichTextEditorComponent;
public tools: ToolbarSettingsModel = {
items: ['Bold', 'Italic', 'Underline', 'StrikeThrough',
'FontName', 'FontSize', 'FontColor', 'BackgroundColor',
'LowerCase', 'UpperCase', '|',
'Formats', 'Alignments', 'OrderedList', 'UnorderedList',
'Outdent', 'Indent', '|',
'CreateLink', 'Image', '|', 'ClearFormat', 'Print',
'SourceCode', 'FullScreen', '|', 'Undo', 'Redo']
};
public value = `<p>The Syncfudion Rich Text Editor, a WYSIWYG (what you see is what you get) editor, is a user interface that allows you to create, edit, and format rich text content. You can try out a demo of this editor here.</p><p><b>Key features:</b></p><ul>
<li>
<p>Provides <IFRAME> and <DIV> modes.</p>
</li>
<li>
<p>Bulleted and numbered lists.</p>
</li>
<li>
<p>Handles images, hyperlinks, videos, hyperlinks, uploads, etc.</p>
</li>
<li>
<p>Contains undo/redo manager. </p>
</li>
</ul><div style="display: inline-block; width: 60%; vertical-align: top; cursor: auto;"><img alt="Sky with sun" src="https://cdn.syncfusion.com/ej2/richtexteditor-resources/RTE-Overview.png" width="309" style="min-width: 10px; min-height: 10px; width: 309px; height: 174px;" class="e-rte-image e-imginline e-rte-drag-image" height="174" /></div> `;
public textArea?: HTMLElement;
public myCodeMirror?: EditorView;
public mirrorConversion(e?: any): void {
const id: string = this.rteObj!.getID() + 'mirror-view';
let mirrorView: HTMLElement = this.rteObj!.element.querySelector('#' + id) as HTMLElement;
if (e.targetItem === 'Preview') {
this.rteObj!.value = this.myCodeMirror!.state.doc.toString();
this.rteObj!.dataBind();
this.rteObj!.rootContainer.classList.remove('e-rte-code-mirror-enabled');
this.rteObj!.focusIn();
} else {
this.rteObj!.rootContainer.classList.add('e-rte-code-mirror-enabled');
this.rteObj!.rootContainer.classList.remove('e-source-code-enabled');
const editorVlaue: string = (this.rteObj!.element.querySelector('.e-rte-srctextarea') as HTMLTextAreaElement).value;
if (!mirrorView) {
mirrorView = createElement('div', { className: 'rte-code-mirror', id: id, styles: 'display: none;' });
this.rteObj!.rootContainer.appendChild(mirrorView);
this.renderCodeMirror(mirrorView, editorVlaue === null ? '' : editorVlaue);
}
else {
this.myCodeMirror!.setState(EditorState.create({doc: editorVlaue, extensions: [basicSetup, html(), EditorView.lineWrapping ]}))
}
this.myCodeMirror!.focus();
}
}
public renderCodeMirror(mirrorView: HTMLElement, content: string): void {
let extensions;
if (document.body.classList && document.body.classList.contains('tailwind3-dark') && document.body.classList.contains('e-dark-mode')) {
// basicSetup: includes line numbers, history, keymaps, etc.
//html(): mode: 'text/html'
extensions = [ basicSetup, html(), EditorView.lineWrapping, oneDark ]
} else {
extensions = [ basicSetup, html(), EditorView.lineWrapping ]
}
const state = EditorState.create({
doc: content,
extensions: extensions
});
this.myCodeMirror = new EditorView({
state,
parent: mirrorView
});
}
public actionCompleteHandler(e: any): void {
if (e.targetItem && (e.targetItem === 'SourceCode' || e.targetItem === 'Preview')) {
this.mirrorConversion(e);
}
}
}.e-rte-code-mirror-enabled .rte-code-mirror {
display: block !important; /* To show the custom source code view. */
}
.e-rte-code-mirror-enabled .e-rte-content {
display: none !important; /* To hide the editor area when custom source code enabled. */
}Additional resources
- GitHub Repository: Angular Rich Text Editor integrations samples
- CodeMirror 6 — Official documentation and guides: https://codemirror.net/6/docs/
- @codemirror packages on npm — state, view, language, and theme modules: https://www.npmjs.com/search?q=%40codemirror
- CodeMirror examples and extensions gallery: https://codemirror.net/6/examples/
- Syncfusion Angular Rich Text Editor — API reference and integration notes: https://ej2.syncfusion.com/angular/documentation/rich-text-editor/
- Syncfusion demos — CodeMirror integration sample: https://ej2.syncfusion.com/angular/demos/#/material/rich-text-editor/overview