Custom Markdown Syntax in React Markdown Editor Component
4 Sep 202524 minutes to read
The React Markdown Editor allows customization of default Markdown syntax to match preferred formatting styles. You can override the default syntax using the formatter property, enabling a customized Markdown experience.
Customizing markdown syntax
You can define custom symbols for different Markdown formatting options:
- Use
+for unordered lists instead of-. - Use
__text__for bold text instead of**text**. - Use
_text_for italic text instead of*text*.
The following example customizes Markdown tags:
[Class-component]
import { createElement } from '@syncfusion/ej2-base';
import { Image, Inject, Link, Table, MarkdownEditor, IToolbarItems, ToolbarSettingsModel, MarkdownFormatter, RichTextEditorComponent, Toolbar } from '@syncfusion/ej2-react-richtexteditor';
import * as React from 'react';
import * as Marked from 'marked';
class App extends React.Component {
rteObj;
// set the value to Rich Text Editor
template = `The sample is added to showcase **markdown editing**.
Type or edit the content and apply formatting to view markdown formatted content.
We can add our own custom formation syntax for the Markdown formation, [sample link](https://ej2.syncfusion.com/home/).
The third-party library <b>Marked</b> is used in this sample to convert markdown into HTML content`;
items = ['Bold', 'Italic', 'StrikeThrough', '|',
'Formats', 'OrderedList', 'UnorderedList', '|',
'CreateLink', 'Image', 'CreateTable',
{
tooltipText: 'Preview',
template: '<button id="preview-code" class="e-tbar-btn e-control e-btn e-icon-btn" aria-label="Preview Code">' +
'<span class="e-btn-icon e-md-preview e-icons"></span></button>'
}, '|', 'Undo', 'Redo'];
textArea;
mdsource;
mdPreview;
toolbarSettings = {
items: this.items
};
formatter = new MarkdownFormatter({
formatTags: {
'Blockquote': '> '
},
listTags: { 'OL': '1., 2., 3.', 'UL': '+ ' },
selectionTags: { 'Bold': '__', 'Italic': '_' }
});
markdownConversion() {
if (this.mdsource.classList.contains('e-active')) {
let id = this.rteObj.getID() + 'html-view';
let htmlPreview = this.rteObj.element.querySelector('#' + id);
htmlPreview.innerHTML = Marked(this.rteObj.contentModule.getEditPanel().value);
}
}
fullPreview() {
let id = this.rteObj.getID() + 'html-preview';
let htmlPreview = this.rteObj.element.querySelector('#' + id);
if (this.mdsource.classList.contains('e-active')) {
this.mdsource.classList.remove('e-active');
this.mdsource.parentElement.title = 'Preview';
this.textArea.style.display = 'block';
htmlPreview.style.display = 'none';
}
else {
this.mdsource.classList.add('e-active');
if (!htmlPreview) {
htmlPreview = createElement('div', { className: 'e-content e-pre-source' });
htmlPreview.id = id;
this.textArea.parentNode.appendChild(htmlPreview);
}
this.textArea.style.display = 'none';
htmlPreview.style.display = 'block';
htmlPreview.innerHTML = Marked(this.rteObj.contentModule.getEditPanel().value);
this.mdsource.parentElement.title = 'Code View';
}
}
rendereComplete() {
if (!this.rteObj || !this.rteObj.contentModule) {
return;
}
this.textArea = this.rteObj.contentModule.getEditPanel();
this.textArea.addEventListener('keyup', (e) => {
this.markdownConversion();
});
this.mdsource = document.getElementById('preview-code');
this.mdsource.addEventListener('click', (e) => {
this.fullPreview();
if (e.currentTarget.classList.contains('e-active')) {
this.rteObj.disableToolbarItem(['Bold', 'Italic', 'StrikeThrough', 'OrderedList',
'UnorderedList', 'SuperScript', 'SubScript', 'CreateLink', 'Image', 'CreateTable', 'Formats', 'Blockquote', 'Undo', 'Redo']);
}
else {
this.rteObj.enableToolbarItem(['Bold', 'Italic', 'StrikeThrough', 'OrderedList',
'UnorderedList', 'SuperScript', 'SubScript', 'CreateLink', 'Image', 'CreateTable', 'Formats', 'Blockquote', 'Undo', 'Redo']);
}
});
}
render() {
return (<div className='control-section' id="rteMarkdown">
<div className="content-wrapper">
<RichTextEditorComponent id="markdownRTE"
ref={(richtexteditor) => { this.rteObj = richtexteditor }} editorMode='Markdown'
height='250px' valueTemplate={this.template} formatter={this.formatter} created={this.rendereComplete} toolbarSettings={this.toolbarSettings} >
<Inject services={[MarkdownEditor, Toolbar, Image, Link, Table]} />
</RichTextEditorComponent>
</div>
</div>);
}
}
export default App;import { createElement, KeyboardEventArgs } from '@syncfusion/ej2-base';
import { Image, Inject, Link, Table, MarkdownEditor, IToolbarItems, ToolbarSettingsModel, MarkdownFormatter, RichTextEditorComponent, Toolbar } from '@syncfusion/ej2-react-richtexteditor';
import * as React from 'react';
import * as Marked from 'marked';
class App extends React.Component<{},{}> {
private rteObj: RichTextEditorComponent;
// set the value to Rich Text Editor
private template: string = `The sample is added to showcase **markdown editing**.
Type or edit the content and apply formatting to view markdown formatted content.
We can add our own custom formation syntax for the Markdown formation, [sample link](https://ej2.syncfusion.com/home/).
The third-party library <b>Marked</b> is used in this sample to convert markdown into HTML content`;
// Rich Text Editor items list
private items: (string | IToolbarItems)[] = ['Bold', 'Italic', 'StrikeThrough', '|',
'Formats', 'OrderedList', 'UnorderedList', '|',
'CreateLink', 'Image', 'CreateTable',
{
tooltipText: 'Preview',
template: '<button id="preview-code" class="e-tbar-btn e-control e-btn e-icon-btn" aria-label="Preview Code">' +
'<span class="e-btn-icon e-md-preview e-icons"></span></button>'
}, '|', 'Undo', 'Redo'];
private textArea: HTMLTextAreaElement;
private mdsource: HTMLElement;
private toolbarSettings: ToolbarSettingsModel = {
items: this.items
};
private formatter: MarkdownFormatter = new MarkdownFormatter({
formatTags: {
'Blockquote': '> '
},
listTags: { 'OL': '1., 2., 3.', 'UL': '+ ' },
selectionTags: { 'Bold': '__', 'Italic': '_' }
});
public markdownConversion(): void {
if (this.mdsource.classList.contains('e-active')) {
let id: string = this.rteObj.getID() + 'html-view';
let htmlPreview: HTMLElement = this.rteObj.element.querySelector('#' + id);
htmlPreview.innerHTML = Marked((this.rteObj.contentModule.getEditPanel() as HTMLTextAreaElement).value);
}
}
public fullPreview(): void {
let id: string = this.rteObj.getID() + 'html-preview';
let htmlPreview: HTMLElement = this.rteObj.element.querySelector('#' + id);
if (this.mdsource.classList.contains('e-active')) {
this.mdsource.classList.remove('e-active');
this.mdsource.parentElement.title = 'Preview';
this.textArea.style.display = 'block';
htmlPreview.style.display = 'none';
} else {
this.mdsource.classList.add('e-active');
if (!htmlPreview) {
htmlPreview = createElement('div', { className: 'e-content e-pre-source' });
htmlPreview.id = id;
this.textArea.parentNode.appendChild(htmlPreview);
}
this.textArea.style.display = 'none';
htmlPreview.style.display = 'block';
htmlPreview.innerHTML = Marked((this.rteObj.contentModule.getEditPanel() as HTMLTextAreaElement).value);
this.mdsource.parentElement.title = 'Code View';
}
}
public rendereComplete(): void {
if (!this.rteObj || !this.rteObj.contentModule) {
return;
}
this.textArea = this.rteObj.contentModule.getEditPanel() as HTMLTextAreaElement;
this.textArea.addEventListener('keyup', (e: KeyboardEventArgs) => {
this.markdownConversion();
});
this.mdsource = document.getElementById('preview-code');
this.mdsource.addEventListener('click', (e: MouseEvent) => {
this.fullPreview();
if ((e.currentTarget as HTMLElement).classList.contains('e-active')) {
this.rteObj.disableToolbarItem(['Bold', 'Italic', 'StrikeThrough', '|',
'Formats', 'OrderedList', 'UnorderedList', '|',
'CreateLink', 'Image', 'CreateTable', 'Undo', 'Redo']);
} else {
this.rteObj.enableToolbarItem(['Bold', 'Italic', 'StrikeThrough', '|',
'Formats', 'OrderedList', 'UnorderedList', '|',
'CreateLink', 'Image', 'CreateTable', 'Undo', 'Redo']);
}
});
}
public render() {
return (
<div className='control-section' id="rteMarkdown">
<div className="content-wrapper">
<RichTextEditorComponent id="markdownRTE" ref={(richtexteditor) => { this.rteObj = richtexteditor; }} editorMode='Markdown'
height='250px' valueTemplate={this.template} formatter={this.formatter} created={this.rendereComplete} toolbarSettings={this.toolbarSettings} >
<Inject services={[MarkdownEditor, Toolbar, Image, Link, Table]} />
</RichTextEditorComponent>
</div>
</div>
);
}
}
export default App;[Functional-component]
import { createElement, KeyboardEventArgs } from '@syncfusion/ej2-base';
import { Image, Inject, Link, Table, MarkdownEditor, IToolbarItems, ToolbarSettingsModel, MarkdownFormatter, RichTextEditorComponent, Toolbar } from '@syncfusion/ej2-react-richtexteditor';
import * as React from 'react';
import * as Marked from 'marked';
function App() {
let rteObj;
const template = `The sample is added to showcase **markdown editing**.
Type or edit the content and apply formatting to view markdown formatted content.
We can add our own custom formation syntax for the Markdown formation, [sample link](https://ej2.syncfusion.com/home/).
The third-party library <b>Marked</b> is used in this sample to convert markdown into HTML content`;
const items = ['Bold', 'Italic', 'StrikeThrough', '|',
'Formats', 'OrderedList', 'UnorderedList', '|',
'CreateLink', 'Image', 'CreateTable',
{
tooltipText: 'Preview',
template: '<button id="preview-code" class="e-tbar-btn e-control e-btn e-icon-btn" aria-label="Preview Code" >' +
'<span class="e-btn-icon e-md-preview e-icons"></span></button>'
}, '|', 'Undo', 'Redo'];
let textArea;
let mdsource;
const toolbarSettings = {
items: items
};
const formatter = new MarkdownFormatter({
formatTags: {
'Blockquote': '> '
},
listTags: { 'OL': '1., 2., 3.', 'UL': '+ ' },
selectionTags: { 'Bold': '__', 'Italic': '_' }
});
function markdownConversion() {
if (mdsource.classList.contains('e-active')) {
let id = rteObj.getID() + 'html-view';
let htmlPreview = rteObj.element.querySelector('#' + id);
htmlPreview.innerHTML = Marked(rteObj.contentModule.getEditPanel().value);
}
}
function fullPreview() {
let id = rteObj.getID() + 'html-preview';
let htmlPreview = rteObj.element.querySelector('#' + id);
if (mdsource.classList.contains('e-active')) {
mdsource.classList.remove('e-active');
mdsource.parentElement.title = 'Preview';
textArea.style.display = 'block';
htmlPreview.style.display = 'none';
}
else {
mdsource.classList.add('e-active');
if (!htmlPreview) {
htmlPreview = createElement('div', { className: 'e-content e-pre-source' });
htmlPreview.id = id;
textArea.parentNode.appendChild(htmlPreview);
}
textArea.style.display = 'none';
htmlPreview.style.display = 'block';
htmlPreview.innerHTML = Marked(rteObj.contentModule.getEditPanel().value);
mdsource.parentElement.title = 'Code View';
}
}
function rendereComplete() {
textArea = rteObj.contentModule.getEditPanel();
textArea.addEventListener('keyup', (e) => {
markdownConversion();
});
mdsource = document.getElementById('preview-code');
mdsource.addEventListener('click', (e) => {
fullPreview();
if (e.currentTarget.classList.contains('e-active')) {
rteObj.disableToolbarItem(['Bold', 'Italic', 'StrikeThrough', '|',
'Formats', 'OrderedList', 'UnorderedList', '|',
'CreateLink', 'Image', 'CreateTable', 'Undo', 'Redo']);
}
else {
rteObj.disableToolbarItem(['Bold', 'Italic', 'StrikeThrough', '|',
'Formats', 'OrderedList', 'UnorderedList', '|',
'CreateLink', 'Image', 'CreateTable', 'Undo', 'Redo']);
}
});
}
return ( <div className='control-section' id="rteMarkdown">
<div className="content-wrapper">
<RichTextEditorComponent id="markdownRTE"
ref={(richtexteditor) => { rteObj = richtexteditor }} editorMode='Markdown'
height='250px' valueTemplate={template} formatter={formatter} created={rendereComplete} toolbarSettings={toolbarSettings} >
<Inject services={[MarkdownEditor, Toolbar, Image, Link, Table]} />
</RichTextEditorComponent>
</div>
</div>);
}
export default App;import { createElement, KeyboardEventArgs } from '@syncfusion/ej2-base';
import { Image, Inject, Link, Table, MarkdownEditor, IToolbarItems, ToolbarSettingsModel, MarkdownFormatter, RichTextEditorComponent, Toolbar } from '@syncfusion/ej2-react-richtexteditor';
import * as React from 'react';
import * as Marked from 'marked';
function App(){
let rteObj: RichTextEditorComponent;
const template: string = `The sample is added to showcase **markdown editing**.
Type or edit the content and apply formatting to view markdown formatted content.
We can add our own custom formation syntax for the Markdown formation, [sample link](https://ej2.syncfusion.com/home/).
The third-party library <b>Marked</b> is used in this sample to convert markdown into HTML content`;
const items: (string | IToolbarItems)[] = ['Bold', 'Italic', 'StrikeThrough', '|',
'Formats', 'OrderedList', 'UnorderedList', '|',
'CreateLink', 'Image', 'CreateTable',
{
tooltipText: 'Preview',
template: '<button id="preview-code" class="e-tbar-btn e-control e-btn e-icon-btn" aria-label="Preview Code" >' +
'<span class="e-btn-icon e-md-preview e-icons"></span></button>'
}, '|', 'Undo', 'Redo'];
let textArea: HTMLTextAreaElement;
let mdsource: HTMLElement;
const toolbarSettings: ToolbarSettingsModel = {
items: items
};
const formatter: MarkdownFormatter = new MarkdownFormatter({
formatTags: {
'Blockquote': '> '
},
listTags: { 'OL': '1., 2., 3.', 'UL': '+ ' },
selectionTags: { 'Bold': '__', 'Italic': '_' }
});
function markdownConversion(): void {
if (mdsource.classList.contains('e-active')) {
let id: string = rteObj.getID() + 'html-view';
let htmlPreview: HTMLElement = rteObj.element.querySelector('#' + id);
htmlPreview.innerHTML = Marked((rteObj.contentModule.getEditPanel() as HTMLTextAreaElement).value);
}
}
function fullPreview(): void {
let id: string = rteObj.getID() + 'html-preview';
let htmlPreview: HTMLElement = rteObj.element.querySelector('#' + id);
if (mdsource.classList.contains('e-active')) {
mdsource.classList.remove('e-active');
mdsource.parentElement.title = 'Preview';
textArea.style.display = 'block';
htmlPreview.style.display = 'none';
} else {
mdsource.classList.add('e-active');
if (!htmlPreview) {
htmlPreview = createElement('div', { className: 'e-content e-pre-source' });
htmlPreview.id = id;
textArea.parentNode.appendChild(htmlPreview);
}
textArea.style.display = 'none';
htmlPreview.style.display = 'block';
htmlPreview.innerHTML = Marked((rteObj.contentModule.getEditPanel() as HTMLTextAreaElement).value);
mdsource.parentElement.title = 'Code View';
}
}
function rendereComplete(): void {
textArea = rteObj.contentModule.getEditPanel() as HTMLTextAreaElement;
textArea.addEventListener('keyup', (e: KeyboardEventArgs) => {
markdownConversion();
});
mdsource = document.getElementById('preview-code');
mdsource.addEventListener('click', (e: MouseEvent) => {
fullPreview();
if ((e.currentTarget as HTMLElement).classList.contains('e-active')) {
rteObj.disableToolbarItem(['Bold', 'Italic', 'StrikeThrough', '|',
'Formats', 'OrderedList', 'UnorderedList', '|',
'CreateLink', 'Image', 'CreateTable', 'Undo', 'Redo']);
} else {
rteObj.enableToolbarItem(['Bold', 'Italic', 'StrikeThrough', '|',
'Formats', 'OrderedList', 'UnorderedList', '|',
'CreateLink', 'Image', 'CreateTable', 'Undo', 'Redo']);
}
});
}
return (
<div className='control-section' id="rteMarkdown">
<div className="content-wrapper">
<RichTextEditorComponent id="markdownRTE"
ref={(richtexteditor) => { rteObj = richtexteditor }} editorMode='Markdown'
height='250px' valueTemplate={template} formatter={formatter} created={rendereComplete} toolbarSettings={toolbarSettings} >
<Inject services={[MarkdownEditor, Toolbar, Image, Link, Table]} />
</RichTextEditorComponent>
</div>
</div>
);
}
export default App;