Custom markdown editor for string properties in Episerver

Markdown is a light-weight text formatting syntax, suitable for multiple channels and content APIs, not just for web pages. In this post we implement a custom Episerver editor to provide users with a WYSIWYG markdown editor.

  • Ted Nyberg
  • 16 September 2016
  • 0

How to use

The editor can be used for any string property, by adding a UIHint attribute:

C# Expand
[Display(Name = "Long description", Description = "Description expressed with markdown")]
public virtual string LongDescription { get; set; }

What it looks like

What it consists of

Our client resources consist of a Dojo widget (Editor.js, Template.html, and Template.css) and an external JavaScript library called SimpleMDE (available on GitHub):


The Editor.js file contains the code for our Dojo widget, i.e. our custom editor:

JavaScript Expand
/* Markdown editor based on SimpleMDE:
   SimpleMDE is provided under MIT license: */





function (



) {
    return declare([_Widget, _TemplatedMixin, _ValueRequiredMixin],
        editor: null, // The SimpleMDE editor object

        templateString: dojo.cache("tedgustaf.editors.markdowneditor", "Template.html"), // Load the widget markup from external template HTML file

        onChange: function (value) {
            /* Summary:
               Notifies Episerver that the property value has changed. */


        constructor: function () {

            /* Summary:
               When the DOM has finished loading, we convert our textarea element to a SimpleMDE editor.
               We also wire up the 'change' event to ensure editor changes propagate to the widget, i.e. property, value.
               We use a timer to introduce a delay before triggering the change event, to avoid "peppering" the
               property value as a user is typing text in the editor. */


            if (config.isDebug) {
                console.log('Setting up SimpleMDE markdown editor...');

            aspect.after(this, "set", function (name, value) {
                if (name === 'value' && value) {
            }, true);

            ready(lang.hitch(this, function () {

                this.editor = new SimpleMDE({
                    element: document.getElementById("editor-" +,
                    initialValue: this.get('value'),
                    placeholder: this.tooltip,
                    spellChecker: false,
                    status: ["lines", "words" ],
                    toolbar: !this.readOnly ? ["bold", "italic", "heading", "unordered-list", "ordered-list", "link", "preview"] : false

                var onChangeDelayTimer;

                this.editor.codemirror.on("change", lang.hitch(this, function () {


                    onChangeDelayTimer = setTimeout(lang.hitch(this, function () {
                        if (!epi.areEqual(this.get('value'), this.editor.value())) {
                            this.set('value', this.editor.value());
                    }), 1000);


        resize: function () {
            /* Summary:
               The resize() function is called when the tab strip containing this widget switches tabs.
               When this happens we need to refresh the editor to ensure it displays property.
               This is a well-known characteristic of CodeMirror, which is part of the SimpleMDE editor. */


        _refreshEditor: function () {
            /* Summary:
               This function refreshes the editor, and ensures its value matches the current property value.
               It also switches to preview mode, making the editor read-only, if the underlying property
               is in read-only mode. */

            if (!this.editor) {

            if (typeof this.get('value') !== 'object' && !epi.areEqual(this.editor.value(), this.get('value'))) {

            if (this.readOnly) {
                var previewElement = this.editor.codemirror.getWrapperElement().lastChild;

                var previewActive = domClass.contains(previewElement, "editor-preview-active");

                if (!previewActive) {
                } else {
                    previewElement.innerHTML = this.editor.options.previewRender(this.editor.value(), previewElement);



 The widget template is quite trivial. It consists of a textarea element (which the SimpleMDE script will turn into a full-blown markdown editor) and some CSS classes to make the editor appear next to the property name in the "All properties" view in edit mode:

HTML, XML Expand
<div class="markdown-editor dijitInline">
    <textarea id="editor-${id}"></textarea>


The stylesheet for our widget template simply sets a suitable width and minimum height for the editor:

CSS Expand
.markdown-editor {
    width: 480px;
    font-family: Verdana, Arial;

.CodeMirror, .CodeMirror-scroll {
    min-height: 200px; /* To make editor height static (i.e. prevent auto-growth), set 'height' to the desired height */

Custom editor descriptor

To enable our custom editor for any string property with our UI hint, we add a class inheriting EditorDescriptor like so:

C# Expand
    TargetType = typeof(string),
    UIHint = UIHint,
    EditorDescriptorBehavior = EditorDescriptorBehavior.PlaceLast)]
public class MarkdownEditorDescriptor : EditorDescriptor
    public const string UIHint = "Markdown";

    public override void ModifyMetadata(ExtendedMetadata metadata, IEnumerable<Attribute> attributes)
        base.ModifyMetadata(metadata, attributes);

        metadata.ClientEditingClass = "tedgustaf.editors.markdowneditor.Editor";

It's functionality is dead simple: it sets ClientEditingClass, i.e. the Dojo widget type to use for editing the property value, to our custom editor widget.

The  Dojo namespace and type names come from the file and folder structure, assuming a root namespace has been defined in module.config in the site root:

HTML, XML Expand
<?xml version="1.0" encoding="utf-8"?>
      <add name="tedgustaf" path="" />