diff --git a/routers/web/repo/commit.go b/routers/web/repo/commit.go
index e012bc24d2..304f76158c 100644
--- a/routers/web/repo/commit.go
+++ b/routers/web/repo/commit.go
@@ -254,7 +254,6 @@ func FileHistory(ctx *context.Context) {
 func Diff(ctx *context.Context) {
 	ctx.Data["PageIsDiff"] = true
 	ctx.Data["RequireHighlightJS"] = true
-	ctx.Data["RequireEasyMDE"] = true
 	ctx.Data["RequireTribute"] = true
 
 	userName := ctx.Repo.Owner.Name
diff --git a/routers/web/repo/compare.go b/routers/web/repo/compare.go
index 3b016b15fb..3b07c35cb0 100644
--- a/routers/web/repo/compare.go
+++ b/routers/web/repo/compare.go
@@ -764,7 +764,6 @@ func CompareDiff(ctx *context.Context) {
 	ctx.Data["IsRepoToolbarCommits"] = true
 	ctx.Data["IsDiffCompare"] = true
 	ctx.Data["RequireTribute"] = true
-	ctx.Data["RequireEasyMDE"] = true
 	setTemplateIfExists(ctx, pullRequestTemplateKey, nil, pullRequestTemplateCandidates)
 	ctx.Data["IsAttachmentEnabled"] = setting.Attachment.Enabled
 	upload.AddUploadContext(ctx, "comment")
diff --git a/routers/web/repo/editor.go b/routers/web/repo/editor.go
index 12098ddc69..1a7edb7bb4 100644
--- a/routers/web/repo/editor.go
+++ b/routers/web/repo/editor.go
@@ -69,7 +69,6 @@ func editFile(ctx *context.Context, isNewFile bool) {
 	ctx.Data["PageIsEdit"] = true
 	ctx.Data["IsNewFile"] = isNewFile
 	ctx.Data["RequireHighlightJS"] = true
-	ctx.Data["RequireEasyMDE"] = true
 	canCommit := renderCommitRights(ctx)
 
 	treePath := cleanUploadFileName(ctx.Repo.TreePath)
@@ -200,7 +199,6 @@ func editFilePost(ctx *context.Context, form forms.EditRepoFileForm, isNewFile b
 	ctx.Data["PageHasPosted"] = true
 	ctx.Data["IsNewFile"] = isNewFile
 	ctx.Data["RequireHighlightJS"] = true
-	ctx.Data["RequireEasyMDE"] = true
 	ctx.Data["TreePath"] = form.TreePath
 	ctx.Data["TreeNames"] = treeNames
 	ctx.Data["TreePaths"] = treePaths
@@ -544,7 +542,6 @@ func DeleteFilePost(ctx *context.Context) {
 func UploadFile(ctx *context.Context) {
 	ctx.Data["PageIsUpload"] = true
 	ctx.Data["RequireTribute"] = true
-	ctx.Data["RequireEasyMDE"] = true
 	upload.AddUploadContext(ctx, "repo")
 	canCommit := renderCommitRights(ctx)
 	treePath := cleanUploadFileName(ctx.Repo.TreePath)
@@ -580,7 +577,6 @@ func UploadFilePost(ctx *context.Context) {
 	form := web.GetForm(ctx).(*forms.UploadRepoFileForm)
 	ctx.Data["PageIsUpload"] = true
 	ctx.Data["RequireTribute"] = true
-	ctx.Data["RequireEasyMDE"] = true
 	upload.AddUploadContext(ctx, "repo")
 	canCommit := renderCommitRights(ctx)
 
diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go
index ea16de3950..a28b37c580 100644
--- a/routers/web/repo/issue.go
+++ b/routers/web/repo/issue.go
@@ -789,7 +789,6 @@ func NewIssue(ctx *context.Context) {
 	ctx.Data["PageIsIssueList"] = true
 	ctx.Data["NewIssueChooseTemplate"] = len(ctx.IssueTemplatesFromDefaultBranch()) > 0
 	ctx.Data["RequireHighlightJS"] = true
-	ctx.Data["RequireEasyMDE"] = true
 	ctx.Data["RequireTribute"] = true
 	ctx.Data["PullRequestWorkInProgressPrefixes"] = setting.Repository.PullRequest.WorkInProgressPrefixes
 	title := ctx.FormString("title")
@@ -962,7 +961,6 @@ func NewIssuePost(ctx *context.Context) {
 	ctx.Data["PageIsIssueList"] = true
 	ctx.Data["NewIssueChooseTemplate"] = len(ctx.IssueTemplatesFromDefaultBranch()) > 0
 	ctx.Data["RequireHighlightJS"] = true
-	ctx.Data["RequireEasyMDE"] = true
 	ctx.Data["PullRequestWorkInProgressPrefixes"] = setting.Repository.PullRequest.WorkInProgressPrefixes
 	ctx.Data["IsAttachmentEnabled"] = setting.Attachment.Enabled
 	upload.AddUploadContext(ctx, "comment")
@@ -1147,7 +1145,6 @@ func ViewIssue(ctx *context.Context) {
 
 	ctx.Data["RequireHighlightJS"] = true
 	ctx.Data["RequireTribute"] = true
-	ctx.Data["RequireEasyMDE"] = true
 	ctx.Data["IsProjectsEnabled"] = ctx.Repo.CanRead(unit.TypeProjects)
 	ctx.Data["IsAttachmentEnabled"] = setting.Attachment.Enabled
 	upload.AddUploadContext(ctx, "comment")
diff --git a/routers/web/repo/pull.go b/routers/web/repo/pull.go
index b40eb1ea17..984954b704 100644
--- a/routers/web/repo/pull.go
+++ b/routers/web/repo/pull.go
@@ -738,7 +738,6 @@ func ViewPullFiles(ctx *context.Context) {
 	setCompareContext(ctx, baseCommit, commit, ctx.Repo.Owner.Name, ctx.Repo.Repository.Name)
 
 	ctx.Data["RequireHighlightJS"] = true
-	ctx.Data["RequireEasyMDE"] = true
 	ctx.Data["RequireTribute"] = true
 	if ctx.Data["Assignees"], err = models.GetRepoAssignees(ctx.Repo.Repository); err != nil {
 		ctx.ServerError("GetAssignees", err)
@@ -1098,7 +1097,6 @@ func CompareAndPullRequestPost(ctx *context.Context) {
 	ctx.Data["IsDiffCompare"] = true
 	ctx.Data["IsRepoToolbarCommits"] = true
 	ctx.Data["RequireTribute"] = true
-	ctx.Data["RequireEasyMDE"] = true
 	ctx.Data["RequireHighlightJS"] = true
 	ctx.Data["PullRequestWorkInProgressPrefixes"] = setting.Repository.PullRequest.WorkInProgressPrefixes
 	ctx.Data["IsAttachmentEnabled"] = setting.Attachment.Enabled
diff --git a/routers/web/repo/release.go b/routers/web/repo/release.go
index 8478cb44ae..f2ab8b85f2 100644
--- a/routers/web/repo/release.go
+++ b/routers/web/repo/release.go
@@ -262,7 +262,6 @@ func LatestRelease(ctx *context.Context) {
 func NewRelease(ctx *context.Context) {
 	ctx.Data["Title"] = ctx.Tr("repo.release.new_release")
 	ctx.Data["PageIsReleaseList"] = true
-	ctx.Data["RequireEasyMDE"] = true
 	ctx.Data["RequireTribute"] = true
 	ctx.Data["tag_target"] = ctx.Repo.Repository.DefaultBranch
 	if tagName := ctx.FormString("tag"); len(tagName) > 0 {
@@ -296,7 +295,6 @@ func NewReleasePost(ctx *context.Context) {
 	form := web.GetForm(ctx).(*forms.NewReleaseForm)
 	ctx.Data["Title"] = ctx.Tr("repo.release.new_release")
 	ctx.Data["PageIsReleaseList"] = true
-	ctx.Data["RequireEasyMDE"] = true
 	ctx.Data["RequireTribute"] = true
 
 	if ctx.HasError() {
@@ -415,7 +413,6 @@ func EditRelease(ctx *context.Context) {
 	ctx.Data["Title"] = ctx.Tr("repo.release.edit_release")
 	ctx.Data["PageIsReleaseList"] = true
 	ctx.Data["PageIsEditRelease"] = true
-	ctx.Data["RequireEasyMDE"] = true
 	ctx.Data["RequireTribute"] = true
 	ctx.Data["IsAttachmentEnabled"] = setting.Attachment.Enabled
 	upload.AddUploadContext(ctx, "release")
@@ -454,7 +451,6 @@ func EditReleasePost(ctx *context.Context) {
 	ctx.Data["Title"] = ctx.Tr("repo.release.edit_release")
 	ctx.Data["PageIsReleaseList"] = true
 	ctx.Data["PageIsEditRelease"] = true
-	ctx.Data["RequireEasyMDE"] = true
 	ctx.Data["RequireTribute"] = true
 
 	tagName := ctx.Params("*")
diff --git a/routers/web/repo/wiki.go b/routers/web/repo/wiki.go
index 7bce7cf11c..d449800b84 100644
--- a/routers/web/repo/wiki.go
+++ b/routers/web/repo/wiki.go
@@ -622,7 +622,6 @@ func WikiRaw(ctx *context.Context) {
 func NewWiki(ctx *context.Context) {
 	ctx.Data["Title"] = ctx.Tr("repo.wiki.new_page")
 	ctx.Data["PageIsWiki"] = true
-	ctx.Data["RequireEasyMDE"] = true
 
 	if !ctx.Repo.Repository.HasWiki() {
 		ctx.Data["title"] = "Home"
@@ -639,7 +638,6 @@ func NewWikiPost(ctx *context.Context) {
 	form := web.GetForm(ctx).(*forms.NewWikiForm)
 	ctx.Data["Title"] = ctx.Tr("repo.wiki.new_page")
 	ctx.Data["PageIsWiki"] = true
-	ctx.Data["RequireEasyMDE"] = true
 
 	if ctx.HasError() {
 		ctx.HTML(http.StatusOK, tplWikiNew)
@@ -677,7 +675,6 @@ func NewWikiPost(ctx *context.Context) {
 func EditWiki(ctx *context.Context) {
 	ctx.Data["PageIsWiki"] = true
 	ctx.Data["PageIsWikiEdit"] = true
-	ctx.Data["RequireEasyMDE"] = true
 
 	if !ctx.Repo.Repository.HasWiki() {
 		ctx.Redirect(ctx.Repo.RepoLink + "/wiki")
@@ -697,7 +694,6 @@ func EditWikiPost(ctx *context.Context) {
 	form := web.GetForm(ctx).(*forms.NewWikiForm)
 	ctx.Data["Title"] = ctx.Tr("repo.wiki.new_page")
 	ctx.Data["PageIsWiki"] = true
-	ctx.Data["RequireEasyMDE"] = true
 
 	if ctx.HasError() {
 		ctx.HTML(http.StatusOK, tplWikiNew)
diff --git a/templates/base/footer.tmpl b/templates/base/footer.tmpl
index 2b641cc9de..122b3b46b4 100644
--- a/templates/base/footer.tmpl
+++ b/templates/base/footer.tmpl
@@ -12,14 +12,6 @@
 	{{template "custom/body_outer_post" .}}
 
 	{{template "base/footer_content" .}}
-{{if .RequireEasyMDE}}
-	<script src="{{AssetUrlPrefix}}/js/easymde.js?v={{MD5 AppVer}}"></script>
-	<script src="{{AssetUrlPrefix}}/vendor/plugins/codemirror/addon/mode/loadmode.js"></script>
-	<script src="{{AssetUrlPrefix}}/vendor/plugins/codemirror/mode/meta.js"></script>
-	<script>
-		CodeMirror.modeURL = '{{AssetUrlPrefix}}/vendor/plugins/codemirror/mode/%N/%N.js';
-	</script>
-{{end}}
 
 <!-- Third-party libraries -->
 {{if .RequireU2F}}
diff --git a/templates/base/head.tmpl b/templates/base/head.tmpl
index 8bc6ae689d..499c113abf 100644
--- a/templates/base/head.tmpl
+++ b/templates/base/head.tmpl
@@ -59,9 +59,6 @@
 	</script>
 	<link rel="icon" href="{{AssetUrlPrefix}}/img/logo.svg" type="image/svg+xml">
 	<link rel="alternate icon" href="{{AssetUrlPrefix}}/img/favicon.png" type="image/png">
-{{if .RequireEasyMDE}}
-	<link rel="stylesheet" href="{{AssetUrlPrefix}}/css/easymde.css?v={{MD5 AppVer}}">
-{{end}}
 	<link rel="stylesheet" href="{{AssetUrlPrefix}}/css/index.css?v={{MD5 AppVer}}">
 	<noscript>
 		<style>
diff --git a/web_src/js/easymde.js b/web_src/js/easymde.js
deleted file mode 100644
index 6bd87a9165..0000000000
--- a/web_src/js/easymde.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import EasyMDE from 'easymde';
-
-import CodeMirror from 'codemirror/lib/codemirror.js';
-
-window.EasyMDE = EasyMDE;
-window.CodeMirror = CodeMirror;
-
diff --git a/web_src/js/features/comp/CommentEasyMDE.js b/web_src/js/features/comp/EasyMDE.js
similarity index 69%
rename from web_src/js/features/comp/CommentEasyMDE.js
rename to web_src/js/features/comp/EasyMDE.js
index d3447d7ba2..d5c1a4c734 100644
--- a/web_src/js/features/comp/CommentEasyMDE.js
+++ b/web_src/js/features/comp/EasyMDE.js
@@ -1,11 +1,58 @@
 import attachTribute from '../tribute.js';
 
+const {appSubUrl} = window.config;
+
+function loadScript(url) {
+  return new Promise((resolve, reject) => {
+    const script = document.createElement('script');
+    script.async = true;
+    script.addEventListener('load', () => {
+      resolve();
+    });
+    script.addEventListener('error', (e) => {
+      reject(e.error);
+    });
+    script.src = url;
+    document.body.appendChild(script);
+  });
+}
+
+/**
+ * @returns {EasyMDE}
+ */
+export async function importEasyMDE() {
+  // for CodeMirror: the plugins should be loaded dynamically
+  // https://github.com/codemirror/CodeMirror/issues/5484
+  // https://github.com/codemirror/CodeMirror/issues/4838
+
+  const [{default: EasyMDE}, {default: CodeMirror}] = await Promise.all([
+    import(/* webpackChunkName: "easymde" */'easymde'),
+    import(/* webpackChunkName: "codemirror" */'codemirror'),
+    import(/* webpackChunkName: "easymde" */'easymde/dist/easymde.min.css'),
+  ]);
+
+  // CodeMirror plugins must be loaded by a "Plain browser env"
+  window.CodeMirror = CodeMirror;
+  await Promise.all([
+    loadScript(`${appSubUrl}/assets/vendor/plugins/codemirror/addon/mode/loadmode.js`),
+    loadScript(`${appSubUrl}/assets/vendor/plugins/codemirror/mode/meta.js`),
+  ]);
+
+  // the loadmode.js/meta.js would set the modeURL/modeInfo properties, so we check it to make sure our loading works
+  if (!CodeMirror.modeURL || !CodeMirror.modeInfo) {
+    throw new Error('failed to load plugins for CodeMirror');
+  }
+
+  CodeMirror.modeURL = `${appSubUrl}/assets/vendor/plugins/codemirror/mode/%N/%N.js`;
+  return EasyMDE;
+}
+
 /**
  * create an EasyMDE editor for comment
  * @param textarea jQuery or HTMLElement
  * @returns {null|EasyMDE}
  */
-export function createCommentEasyMDE(textarea) {
+export async function createCommentEasyMDE(textarea) {
   if (textarea instanceof jQuery) {
     textarea = textarea[0];
   }
@@ -13,12 +60,13 @@ export function createCommentEasyMDE(textarea) {
     return null;
   }
 
-  const easyMDE = new window.EasyMDE({
+  const EasyMDE = await importEasyMDE();
+  const easyMDE = new EasyMDE({
     autoDownloadFontAwesome: false,
     element: textarea,
     forceSync: true,
     renderingConfig: {
-      singleLineBreaks: false
+      singleLineBreaks: false,
     },
     indentWithTabs: false,
     tabSize: 4,
@@ -56,7 +104,7 @@ export function createCommentEasyMDE(textarea) {
         className: 'fa fa-file',
         title: 'Revert to simple textarea',
       },
-    ]
+    ],
   });
   const inputField = easyMDE.codemirror.getInputField();
   inputField.classList.add('js-quick-submit');
@@ -64,7 +112,7 @@ export function createCommentEasyMDE(textarea) {
     Enter: () => {
       const tributeContainer = document.querySelector('.tribute-container');
       if (!tributeContainer || tributeContainer.style.display === 'none') {
-        return CodeMirror.Pass;
+        return window.CodeMirror.Pass;
       }
     },
     Backspace: (cm) => {
@@ -72,7 +120,7 @@ export function createCommentEasyMDE(textarea) {
         cm.getInputField().trigger('input');
       }
       cm.execCommand('delCharBefore');
-    }
+    },
   });
   attachTribute(inputField, {mentions: true, emoji: true});
   attachEasyMDEToElements(easyMDE);
diff --git a/web_src/js/features/repo-diff.js b/web_src/js/features/repo-diff.js
index 3378222ae1..3d937bbdb1 100644
--- a/web_src/js/features/repo-diff.js
+++ b/web_src/js/features/repo-diff.js
@@ -1,6 +1,6 @@
 import {initCompReactionSelector} from './comp/ReactionSelector.js';
 import {initRepoIssueContentHistory} from './repo-issue-content.js';
-import {validateTextareaNonEmpty} from './comp/CommentEasyMDE.js';
+import {validateTextareaNonEmpty} from './comp/EasyMDE.js';
 const {csrfToken} = window.config;
 
 export function initRepoDiffReviewButton() {
diff --git a/web_src/js/features/repo-issue.js b/web_src/js/features/repo-issue.js
index 545f59a4d8..c2b0254a81 100644
--- a/web_src/js/features/repo-issue.js
+++ b/web_src/js/features/repo-issue.js
@@ -1,6 +1,6 @@
 import {htmlEscape} from 'escape-goat';
 import attachTribute from './tribute.js';
-import {createCommentEasyMDE, getAttachedEasyMDE} from './comp/CommentEasyMDE.js';
+import {createCommentEasyMDE, getAttachedEasyMDE} from './comp/EasyMDE.js';
 import {initCompImagePaste} from './comp/ImagePaste.js';
 import {initCompMarkupContentPreviewTab} from './comp/MarkupContentPreview.js';
 
@@ -439,16 +439,17 @@ export function initRepoPullRequestReview() {
     $(`#show-outdated-${id}`).removeClass('hide');
   });
 
-  $(document).on('click', 'button.comment-form-reply', function (e) {
+  $(document).on('click', 'button.comment-form-reply', async function (e) {
     e.preventDefault();
+
     $(this).hide();
     const form = $(this).closest('.comment-code-cloud').find('.comment-form');
     form.removeClass('hide');
     const $textarea = form.find('textarea');
     let easyMDE = getAttachedEasyMDE($textarea);
     if (!easyMDE) {
-      attachTribute($textarea.get(), {mentions: true, emoji: true});
-      easyMDE = createCommentEasyMDE($textarea);
+      await attachTribute($textarea.get(), {mentions: true, emoji: true});
+      easyMDE = await createCommentEasyMDE($textarea);
     }
     $textarea.focus();
     easyMDE.codemirror.focus();
@@ -515,8 +516,8 @@ export function initRepoPullRequestReview() {
       td.find("input[name='side']").val(side === 'left' ? 'previous' : 'proposed');
       td.find("input[name='path']").val(path);
       const $textarea = commentCloud.find('textarea');
-      attachTribute($textarea.get(), {mentions: true, emoji: true});
-      const easyMDE = createCommentEasyMDE($textarea);
+      await attachTribute($textarea.get(), {mentions: true, emoji: true});
+      const easyMDE = await createCommentEasyMDE($textarea);
       $textarea.focus();
       easyMDE.codemirror.focus();
     }
diff --git a/web_src/js/features/repo-legacy.js b/web_src/js/features/repo-legacy.js
index 87d311716a..fccec8ccac 100644
--- a/web_src/js/features/repo-legacy.js
+++ b/web_src/js/features/repo-legacy.js
@@ -1,4 +1,4 @@
-import {createCommentEasyMDE, getAttachedEasyMDE} from './comp/CommentEasyMDE.js';
+import {createCommentEasyMDE, getAttachedEasyMDE} from './comp/EasyMDE.js';
 import {initCompMarkupContentPreviewTab} from './comp/MarkupContentPreview.js';
 import {initCompImagePaste, initEasyMDEImagePaste} from './comp/ImagePaste.js';
 import {
@@ -256,6 +256,7 @@ export function initRepoCommentForm() {
 
 async function onEditContent(event) {
   event.preventDefault();
+
   $(this).closest('.dropdown').find('.menu').toggle('visible');
   const $segment = $(this).closest('.header').next();
   const $editContentZone = $segment.find('.edit-content-zone');
@@ -341,7 +342,7 @@ async function onEditContent(event) {
     $tabMenu.find('.preview.item').attr('data-tab', $editContentZone.data('preview'));
     $editContentForm.find('.write').attr('data-tab', $editContentZone.data('write'));
     $editContentForm.find('.preview').attr('data-tab', $editContentZone.data('preview'));
-    easyMDE = createCommentEasyMDE($textarea);
+    easyMDE = await createCommentEasyMDE($textarea);
 
     initCompMarkupContentPreviewTab($editContentForm);
     if ($dropzone.length === 1) {
diff --git a/web_src/js/features/repo-release.js b/web_src/js/features/repo-release.js
index f69ce37d6b..915e722546 100644
--- a/web_src/js/features/repo-release.js
+++ b/web_src/js/features/repo-release.js
@@ -1,7 +1,7 @@
 import attachTribute from './tribute.js';
 import {initCompMarkupContentPreviewTab} from './comp/MarkupContentPreview.js';
 import {initEasyMDEImagePaste} from './comp/ImagePaste.js';
-import {createCommentEasyMDE} from './comp/CommentEasyMDE.js';
+import {createCommentEasyMDE} from './comp/EasyMDE.js';
 
 export function initRepoRelease() {
   $(document).on('click', '.remove-rel-attach', function() {
@@ -19,11 +19,13 @@ export function initRepoReleaseEditor() {
     return false;
   }
 
-  const $textarea = $editor.find('textarea');
-  attachTribute($textarea.get(), {mentions: false, emoji: true});
-  const $files = $editor.parent().find('.files');
-  const easyMDE = createCommentEasyMDE($textarea);
-  initCompMarkupContentPreviewTab($editor);
-  const dropzone = $editor.parent().find('.dropzone')[0];
-  initEasyMDEImagePaste(easyMDE, dropzone, $files);
+  (async () => {
+    const $textarea = $editor.find('textarea');
+    await attachTribute($textarea.get(), {mentions: false, emoji: true});
+    const $files = $editor.parent().find('.files');
+    const easyMDE = await createCommentEasyMDE($textarea);
+    initCompMarkupContentPreviewTab($editor);
+    const dropzone = $editor.parent().find('.dropzone')[0];
+    initEasyMDEImagePaste(easyMDE, dropzone, $files);
+  })();
 }
diff --git a/web_src/js/features/repo-wiki.js b/web_src/js/features/repo-wiki.js
index b76dac030e..c0dc8d1b41 100644
--- a/web_src/js/features/repo-wiki.js
+++ b/web_src/js/features/repo-wiki.js
@@ -1,186 +1,191 @@
 import {initMarkupContent} from '../markup/content.js';
-import {attachEasyMDEToElements, validateTextareaNonEmpty} from './comp/CommentEasyMDE.js';
+import {attachEasyMDEToElements, importEasyMDE, validateTextareaNonEmpty} from './comp/EasyMDE.js';
 import {initCompMarkupContentPreviewTab} from './comp/MarkupContentPreview.js';
 
 const {csrfToken} = window.config;
 
-export function initRepoWikiForm() {
+async function initRepoWikiFormEditor() {
   const $editArea = $('.repository.wiki textarea#edit_area');
+  if (!$editArea.length) return;
+
   let sideBySideChanges = 0;
   let sideBySideTimeout = null;
   let hasEasyMDE = true;
 
-  if ($editArea.length > 0) {
-    const $form = $('.repository.wiki.new .ui.form');
-    const easyMDE = new window.EasyMDE({
-      autoDownloadFontAwesome: false,
-      element: $editArea[0],
-      forceSync: true,
-      previewRender(plainText, preview) { // Async method
-        // FIXME: still send render request when return back to edit mode
-        const render = function () {
-          sideBySideChanges = 0;
+  const $form = $('.repository.wiki.new .ui.form');
+  const EasyMDE = await importEasyMDE();
+  const easyMDE = new EasyMDE({
+    autoDownloadFontAwesome: false,
+    element: $editArea[0],
+    forceSync: true,
+    previewRender(plainText, preview) { // Async method
+      // FIXME: still send render request when return back to edit mode
+      const render = function () {
+        sideBySideChanges = 0;
+        if (sideBySideTimeout !== null) {
+          clearTimeout(sideBySideTimeout);
+          sideBySideTimeout = null;
+        }
+        $.post($editArea.data('url'), {
+          _csrf: csrfToken,
+          mode: 'gfm',
+          context: $editArea.data('context'),
+          text: plainText,
+          wiki: true
+        }, (data) => {
+          preview.innerHTML = `<div class="markup ui segment">${data}</div>`;
+          initMarkupContent();
+        });
+      };
+
+      setTimeout(() => {
+        if (!easyMDE.isSideBySideActive()) {
+          render();
+        } else {
+          // delay preview by keystroke counting
+          sideBySideChanges++;
+          if (sideBySideChanges > 10) {
+            render();
+          }
+          // or delay preview by timeout
           if (sideBySideTimeout !== null) {
             clearTimeout(sideBySideTimeout);
             sideBySideTimeout = null;
           }
-          $.post($editArea.data('url'), {
-            _csrf: csrfToken,
-            mode: 'gfm',
-            context: $editArea.data('context'),
-            text: plainText,
-            wiki: true
-          }, (data) => {
-            preview.innerHTML = `<div class="markup ui segment">${data}</div>`;
-            initMarkupContent();
-          });
-        };
-
-        setTimeout(() => {
-          if (!easyMDE.isSideBySideActive()) {
-            render();
-          } else {
-            // delay preview by keystroke counting
-            sideBySideChanges++;
-            if (sideBySideChanges > 10) {
-              render();
-            }
-            // or delay preview by timeout
-            if (sideBySideTimeout !== null) {
-              clearTimeout(sideBySideTimeout);
-              sideBySideTimeout = null;
-            }
-            sideBySideTimeout = setTimeout(render, 600);
-          }
-        }, 0);
-        if (!easyMDE.isSideBySideActive()) {
-          return 'Loading...';
+          sideBySideTimeout = setTimeout(render, 600);
         }
-        return preview.innerHTML;
-      },
-      renderingConfig: {
-        singleLineBreaks: false
-      },
-      indentWithTabs: false,
-      tabSize: 4,
-      spellChecker: false,
-      toolbar: ['bold', 'italic', 'strikethrough', '|',
-        'heading-1', 'heading-2', 'heading-3', 'heading-bigger', 'heading-smaller', '|',
-        {
-          name: 'code-inline',
-          action(e) {
-            const cm = e.codemirror;
-            const selection = cm.getSelection();
-            cm.replaceSelection(`\`${selection}\``);
-            if (!selection) {
-              const cursorPos = cm.getCursor();
-              cm.setCursor(cursorPos.line, cursorPos.ch - 1);
-            }
-            cm.focus();
-          },
-          className: 'fa fa-angle-right',
-          title: 'Add Inline Code',
-        }, 'code', 'quote', '|', {
-          name: 'checkbox-empty',
-          action(e) {
-            const cm = e.codemirror;
-            cm.replaceSelection(`\n- [ ] ${cm.getSelection()}`);
-            cm.focus();
-          },
-          className: 'fa fa-square-o',
-          title: 'Add Checkbox (empty)',
+      }, 0);
+      if (!easyMDE.isSideBySideActive()) {
+        return 'Loading...';
+      }
+      return preview.innerHTML;
+    },
+    renderingConfig: {
+      singleLineBreaks: false
+    },
+    indentWithTabs: false,
+    tabSize: 4,
+    spellChecker: false,
+    toolbar: ['bold', 'italic', 'strikethrough', '|',
+      'heading-1', 'heading-2', 'heading-3', 'heading-bigger', 'heading-smaller', '|',
+      {
+        name: 'code-inline',
+        action(e) {
+          const cm = e.codemirror;
+          const selection = cm.getSelection();
+          cm.replaceSelection(`\`${selection}\``);
+          if (!selection) {
+            const cursorPos = cm.getCursor();
+            cm.setCursor(cursorPos.line, cursorPos.ch - 1);
+          }
+          cm.focus();
         },
-        {
-          name: 'checkbox-checked',
-          action(e) {
-            const cm = e.codemirror;
-            cm.replaceSelection(`\n- [x] ${cm.getSelection()}`);
-            cm.focus();
-          },
-          className: 'fa fa-check-square-o',
-          title: 'Add Checkbox (checked)',
-        }, '|',
-        'unordered-list', 'ordered-list', '|',
-        'link', 'image', 'table', 'horizontal-rule', '|',
-        'clean-block', 'preview', 'fullscreen', 'side-by-side', '|',
-        {
-          name: 'revert-to-textarea',
-          action(e) {
-            e.toTextArea();
-            hasEasyMDE = false;
-            const $root = $form.find('.field.content');
-            const loading = $root.data('loading');
-            $root.append(`<div class="ui bottom tab markup" data-tab="preview">${loading}</div>`);
-            initCompMarkupContentPreviewTab($form);
-          },
-          className: 'fa fa-file',
-          title: 'Revert to simple textarea',
+        className: 'fa fa-angle-right',
+        title: 'Add Inline Code',
+      }, 'code', 'quote', '|', {
+        name: 'checkbox-empty',
+        action(e) {
+          const cm = e.codemirror;
+          cm.replaceSelection(`\n- [ ] ${cm.getSelection()}`);
+          cm.focus();
         },
-      ]
-    });
+        className: 'fa fa-square-o',
+        title: 'Add Checkbox (empty)',
+      },
+      {
+        name: 'checkbox-checked',
+        action(e) {
+          const cm = e.codemirror;
+          cm.replaceSelection(`\n- [x] ${cm.getSelection()}`);
+          cm.focus();
+        },
+        className: 'fa fa-check-square-o',
+        title: 'Add Checkbox (checked)',
+      }, '|',
+      'unordered-list', 'ordered-list', '|',
+      'link', 'image', 'table', 'horizontal-rule', '|',
+      'clean-block', 'preview', 'fullscreen', 'side-by-side', '|',
+      {
+        name: 'revert-to-textarea',
+        action(e) {
+          e.toTextArea();
+          hasEasyMDE = false;
+          const $root = $form.find('.field.content');
+          const loading = $root.data('loading');
+          $root.append(`<div class="ui bottom tab markup" data-tab="preview">${loading}</div>`);
+          initCompMarkupContentPreviewTab($form);
+        },
+        className: 'fa fa-file',
+        title: 'Revert to simple textarea',
+      },
+    ]
+  });
 
-    attachEasyMDEToElements(easyMDE);
+  attachEasyMDEToElements(easyMDE);
 
-    const $mdeInputField = $(easyMDE.codemirror.getInputField());
-    $mdeInputField.addClass('js-quick-submit');
+  const $mdeInputField = $(easyMDE.codemirror.getInputField());
+  $mdeInputField.addClass('js-quick-submit');
 
-    $form.on('submit', () => {
-      if (!validateTextareaNonEmpty($editArea)) {
+  $form.on('submit', () => {
+    if (!validateTextareaNonEmpty($editArea)) {
+      return false;
+    }
+  });
+
+  setTimeout(() => {
+    const $bEdit = $('.repository.wiki.new .previewtabs a[data-tab="write"]');
+    const $bPrev = $('.repository.wiki.new .previewtabs a[data-tab="preview"]');
+    const $toolbar = $('.editor-toolbar');
+    const $bPreview = $('.editor-toolbar button.preview');
+    const $bSideBySide = $('.editor-toolbar a.fa-columns');
+    $bEdit.on('click', (e) => {
+      if (!hasEasyMDE) {
         return false;
       }
+      e.stopImmediatePropagation();
+      if ($toolbar.hasClass('disabled-for-preview')) {
+        $bPreview.trigger('click');
+      }
+
+      return false;
     });
-
-    setTimeout(() => {
-      const $bEdit = $('.repository.wiki.new .previewtabs a[data-tab="write"]');
-      const $bPrev = $('.repository.wiki.new .previewtabs a[data-tab="preview"]');
-      const $toolbar = $('.editor-toolbar');
-      const $bPreview = $('.editor-toolbar button.preview');
-      const $bSideBySide = $('.editor-toolbar a.fa-columns');
-      $bEdit.on('click', (e) => {
-        if (!hasEasyMDE) {
-          return false;
-        }
-        e.stopImmediatePropagation();
+    $bPrev.on('click', (e) => {
+      if (!hasEasyMDE) {
+        return false;
+      }
+      e.stopImmediatePropagation();
+      if (!$toolbar.hasClass('disabled-for-preview')) {
+        $bPreview.trigger('click');
+      }
+      return false;
+    });
+    $bPreview.on('click', () => {
+      setTimeout(() => {
         if ($toolbar.hasClass('disabled-for-preview')) {
-          $bPreview.trigger('click');
-        }
-
-        return false;
-      });
-      $bPrev.on('click', (e) => {
-        if (!hasEasyMDE) {
-          return false;
-        }
-        e.stopImmediatePropagation();
-        if (!$toolbar.hasClass('disabled-for-preview')) {
-          $bPreview.trigger('click');
-        }
-        return false;
-      });
-      $bPreview.on('click', () => {
-        setTimeout(() => {
-          if ($toolbar.hasClass('disabled-for-preview')) {
-            if ($bEdit.hasClass('active')) {
-              $bEdit.removeClass('active');
-            }
-            if (!$bPrev.hasClass('active')) {
-              $bPrev.addClass('active');
-            }
-          } else {
-            if (!$bEdit.hasClass('active')) {
-              $bEdit.addClass('active');
-            }
-            if ($bPrev.hasClass('active')) {
-              $bPrev.removeClass('active');
-            }
+          if ($bEdit.hasClass('active')) {
+            $bEdit.removeClass('active');
           }
-        }, 0);
+          if (!$bPrev.hasClass('active')) {
+            $bPrev.addClass('active');
+          }
+        } else {
+          if (!$bEdit.hasClass('active')) {
+            $bEdit.addClass('active');
+          }
+          if ($bPrev.hasClass('active')) {
+            $bPrev.removeClass('active');
+          }
+        }
+      }, 0);
 
-        return false;
-      });
-      $bSideBySide.on('click', () => {
-        sideBySideChanges = 10;
-      });
-    }, 0);
-  }
+      return false;
+    });
+    $bSideBySide.on('click', () => {
+      sideBySideChanges = 10;
+    });
+  }, 0);
+}
+
+export function initRepoWikiForm() {
+  initRepoWikiFormEditor();
 }
diff --git a/webpack.config.js b/webpack.config.js
index 2224ad6eda..b3887f723d 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -60,10 +60,6 @@ export default {
     'eventsource.sharedworker': [
       resolve(__dirname, 'web_src/js/features/eventsource.sharedworker.js'),
     ],
-    'easymde': [
-      resolve(__dirname, 'web_src/js/easymde.js'),
-      resolve(__dirname, 'node_modules/easymde/dist/easymde.min.css'),
-    ],
     ...themes,
   },
   devtool: false,