diff --git a/resources/assets/js/code.js b/resources/assets/js/code.js
index 83cb664a1..ef6bca2e2 100644
--- a/resources/assets/js/code.js
+++ b/resources/assets/js/code.js
@@ -62,7 +62,7 @@ function highlightElem(elem) {
     let mode = '';
     if (innerCodeElem !== null) {
         let langName = innerCodeElem.className.replace('language-', '');
-        if (typeof modeMap[langName] !== 'undefined') mode = modeMap[langName];
+        mode = getMode(langName);
     }
     elem.innerHTML = elem.innerHTML.replace(/<br\s*[\/]?>/gi ,'\n');
     let content = elem.textContent;
@@ -78,16 +78,35 @@ function highlightElem(elem) {
     });
 }
 
+/**
+ * Search for a codemirror code based off a user suggestion
+ * @param suggestion
+ * @returns {string}
+ */
+function getMode(suggestion) {
+    suggestion = suggestion.trim().replace(/^\./g, '').toLowerCase();
+    return (typeof modeMap[suggestion] !== 'undefined') ? modeMap[suggestion] : '';
+}
+
 module.exports.highlightElem = highlightElem;
 
 module.exports.wysiwygView = function(elem) {
     let doc = elem.ownerDocument;
+    let codeElem = elem.querySelector('code');
+
+    let lang = (elem.className || '').replace('language-', '');
+    if (lang === '' && codeElem) {
+        console.log(codeElem.className);
+        lang = (codeElem.className || '').replace('language-', '')
+    }
+
     elem.innerHTML = elem.innerHTML.replace(/<br\s*[\/]?>/gi ,'\n');
     let content = elem.textContent;
     let newWrap = doc.createElement('div');
     let newTextArea = doc.createElement('textarea');
 
     newWrap.className = 'CodeMirrorContainer';
+    newWrap.setAttribute('data-lang', lang);
     newTextArea.style.display = 'none';
     elem.parentNode.replaceChild(newWrap, elem);
 
@@ -99,7 +118,7 @@ module.exports.wysiwygView = function(elem) {
         newWrap.appendChild(elt);
     }, {
         value: content,
-        mode:  '',
+        mode:  getMode(lang),
         lineNumbers: true,
         theme: 'base16-light',
         readOnly: true
@@ -107,50 +126,47 @@ module.exports.wysiwygView = function(elem) {
     setTimeout(() => {
         cm.refresh();
     }, 300);
-    return newWrap;
+    return {wrap: newWrap, editor: cm};
 };
 
-// module.exports.wysiwygEditor = function(elem) {
-//     let doc = elem.ownerDocument;
-//     let newWrap = doc.createElement('div');
-//     newWrap.className = 'CodeMirrorContainer';
-//     let newTextArea = doc.createElement('textarea');
-//     newTextArea.style.display = 'none';
-//     elem.innerHTML = elem.innerHTML.replace(/<br\s*[\/]?>/gi ,'\n');
-//     let content = elem.textContent;
-//     elem.parentNode.replaceChild(newWrap, elem);
-//     newWrap.appendChild(newTextArea);
-//     let cm = CodeMirror(function(elt) {
-//         newWrap.appendChild(elt);
-//     }, {
-//         value: content,
-//         mode:  '',
-//         lineNumbers: true,
-//         theme: 'base16-light',
-//         readOnly: true
-//     });
-//     cm.on('change', event => {
-//         newTextArea.innerText = cm.getValue();
-//     });
-//     setTimeout(() => {
-//         cm.refresh();
-//     }, 300);
-// };
-
-module.exports.markdownEditor = function(elem) {
+module.exports.popupEditor = function(elem, modeSuggestion) {
     let content = elem.textContent;
 
-    let cm = CodeMirror(function(elt) {
+    return CodeMirror(function(elt) {
         elem.parentNode.insertBefore(elt, elem);
         elem.style.display = 'none';
     }, {
         value: content,
-        mode:  "markdown",
+        mode:  getMode(modeSuggestion),
+        lineNumbers: true,
+        theme: 'base16-light',
+        lineWrapping: true
+    });
+};
+
+module.exports.setMode = function(cmInstance, modeSuggestion) {
+      cmInstance.setOption('mode', getMode(modeSuggestion));
+};
+module.exports.setContent = function(cmInstance, codeContent) {
+    cmInstance.setValue(codeContent);
+    setTimeout(() => {
+        cmInstance.refresh();
+    }, 10);
+};
+
+module.exports.markdownEditor = function(elem) {
+    let content = elem.textContent;
+
+    return CodeMirror(function (elt) {
+        elem.parentNode.insertBefore(elt, elem);
+        elem.style.display = 'none';
+    }, {
+        value: content,
+        mode: "markdown",
         lineNumbers: true,
         theme: 'base16-light',
         lineWrapping: true
     });
-    return cm;
 
 };
 
diff --git a/resources/assets/js/pages/page-form.js b/resources/assets/js/pages/page-form.js
index 871d2b528..a443213bf 100644
--- a/resources/assets/js/pages/page-form.js
+++ b/resources/assets/js/pages/page-form.js
@@ -58,11 +58,14 @@ function registerEditorShortcuts(editor) {
     // Other block shortcuts
     editor.addShortcut('meta+q', '', ['FormatBlock', false, 'blockquote']);
     editor.addShortcut('meta+d', '', ['FormatBlock', false, 'p']);
-    editor.addShortcut('meta+e', '', ['FormatBlock', false, 'pre']);
+    editor.addShortcut('meta+e', '', ['codeeditor', false, 'pre']);
     editor.addShortcut('meta+shift+E', '', ['FormatBlock', false, 'code']);
 }
 
 
+/**
+ * Create and enable our custom code plugin
+ */
 function codePlugin() {
 
     function elemIsCodeBlock(elem) {
@@ -71,14 +74,35 @@ function codePlugin() {
 
     function showPopup(editor) {
         let selectedNode = editor.selection.getNode();
+
         if (!elemIsCodeBlock(selectedNode)) {
+            let providedCode = editor.selection.getNode().textContent;
+            window.vues['code-editor'].open(providedCode, '', (code, lang) => {
+                let wrap = document.createElement('div');
+                wrap.innerHTML = `<pre><code class="language-${lang}"></code></pre>`;
+                wrap.querySelector('code').innerText = code;
+                editor.formatter.toggle('pre');
+                let node = editor.selection.getNode();
+                editor.dom.setHTML(node, wrap.querySelector('pre').innerHTML);
+                editor.fire('SetContent');
+            });
             return;
         }
 
-        let lang = selectedNode.hasAttribute('data-language') ? selectedNode.getAttribute('data-language') : '';
+        let lang = selectedNode.hasAttribute('data-lang') ? selectedNode.getAttribute('data-lang') : '';
         let currentCode = selectedNode.querySelector('textarea').textContent;
-        console.log('SHOW POPUP');
-        // TODO - Show custom editor
+
+        window.vues['code-editor'].open(currentCode, lang, (code, lang) => {
+            let editorElem = selectedNode.querySelector('.CodeMirror');
+            let cmInstance = editorElem.CodeMirror;
+            if (cmInstance) {
+                Code.setContent(cmInstance, code);
+                Code.setMode(cmInstance, lang);
+            }
+            let textArea = selectedNode.querySelector('textarea');
+            if (textArea) textArea.textContent = code;
+            selectedNode.setAttribute('data-lang', lang);
+        });
     }
 
     window.tinymce.PluginManager.add('codeeditor', (editor, url) => {
@@ -88,9 +112,11 @@ function codePlugin() {
         editor.addButton('codeeditor', {
             text: 'Code block',
             icon: false,
-            onclick() {
-                showPopup(editor);
-            }
+            cmd: 'codeeditor'
+        });
+
+        editor.addCommand('codeeditor', () => {
+            showPopup(editor);
         });
 
         // Convert
@@ -98,32 +124,33 @@ function codePlugin() {
             $('div.CodeMirrorContainer', e.node).
             each((index, elem) => {
                 let $elem = $(elem);
-                let code = elem.querySelector('textarea').textContent;
+                let textArea = elem.querySelector('textarea');
+                let code = textArea.textContent;
+                let lang = elem.getAttribute('data-lang');
 
                 // $elem.attr('class', $.trim($elem.attr('class')));
                 $elem.removeAttr('contentEditable');
-
-                $elem.empty().append('<pre></pre>').find('pre').first().append($('<code></code>').each((index, elem) => {
+                let $pre = $('<pre></pre>');
+                $pre.append($('<code></code>').each((index, elem) => {
                     // Needs to be textContent since innerText produces BR:s
                     elem.textContent = code;
-                }).attr('class', $elem.attr('class')));
-                console.log($elem[0].outerHTML);
+                }).attr('class', `language-${lang}`));
+                $elem.replaceWith($pre);
             });
         });
 
         editor.on('SetContent', function () {
-            let codeSamples = $('pre').filter((index, elem) => {
+            let codeSamples = $('body > pre').filter((index, elem) => {
                 return elem.contentEditable !== "false";
             });
 
             if (codeSamples.length) {
                 editor.undoManager.transact(function () {
                     codeSamples.each((index, elem) => {
-                        console.log(elem.textContent);
-                        let outerWrap = Code.wysiwygView(elem);
-                        outerWrap.addEventListener('dblclick', () => {
-                            showPopup(editor);
-                        })
+                        let editDetails = Code.wysiwygView(elem);
+                        editDetails.wrap.addEventListener('dblclick', () => {
+                            showPopup(editor, editDetails.wrap, editDetails.editor);
+                        });
                     });
                 });
             }
@@ -154,7 +181,7 @@ module.exports = function() {
         valid_children: "-div[p|pre|h1|h2|h3|h4|h5|h6|blockquote]",
         plugins: "image table textcolor paste link autolink fullscreen imagetools code customhr autosave lists codeeditor",
         imagetools_toolbar: 'imageoptions',
-        toolbar: "undo redo | styleselect | bold italic underline strikethrough superscript subscript | forecolor backcolor | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | table image-insert link hr | removeformat code fullscreen codeeditor",
+        toolbar: "undo redo | styleselect | bold italic underline strikethrough superscript subscript | forecolor backcolor | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | table image-insert link hr | removeformat code fullscreen",
         content_style: "body {padding-left: 15px !important; padding-right: 15px !important; margin:0!important; margin-left:auto!important;margin-right:auto!important;}",
         style_formats: [
             {title: "Header Large", format: "h2"},
@@ -163,14 +190,14 @@ module.exports = function() {
             {title: "Header Tiny", format: "h5"},
             {title: "Paragraph", format: "p", exact: true, classes: ''},
             {title: "Blockquote", format: "blockquote"},
-            {title: "Code Block", icon: "code", format: "pre"},
+            {title: "Code Block", icon: "code", cmd: 'codeeditor'},
             {title: "Inline Code", icon: "code", inline: "code"},
             {title: "Callouts", items: [
                 {title: "Success", block: 'p', exact: true, attributes : {'class' : 'callout success'}},
                 {title: "Info", block: 'p', exact: true, attributes : {'class' : 'callout info'}},
                 {title: "Warning", block: 'p', exact: true, attributes : {'class' : 'callout warning'}},
                 {title: "Danger", block: 'p', exact: true, attributes : {'class' : 'callout danger'}}
-            ]}
+            ]},
         ],
         style_formats_merge: false,
         formats: {
diff --git a/resources/assets/js/vues/code-editor.js b/resources/assets/js/vues/code-editor.js
new file mode 100644
index 000000000..87bb28cce
--- /dev/null
+++ b/resources/assets/js/vues/code-editor.js
@@ -0,0 +1,39 @@
+const codeLib = require('../code');
+
+const methods = {
+    show() {
+        if (!this.editor) this.editor = codeLib.popupEditor(this.$refs.editor, this.language);
+        this.$refs.overlay.style.display = 'flex';
+    },
+    hide() {
+        this.$refs.overlay.style.display = 'none';
+    },
+    updateEditorMode(language) {
+        codeLib.setMode(this.editor, language);
+    },
+    open(code, language, callback) {
+        this.show();
+        this.updateEditorMode(language);
+        this.language = language;
+        codeLib.setContent(this.editor, code);
+        this.code = code;
+        this.callback = callback;
+    },
+    save() {
+        if (!this.callback) return;
+        this.callback(this.editor.getValue(), this.language);
+        this.hide();
+    }
+};
+
+const data = {
+    editor: null,
+    language: '',
+    code: '',
+    callback: null
+};
+
+module.exports = {
+    methods,
+    data
+};
\ No newline at end of file
diff --git a/resources/assets/js/vues/vues.js b/resources/assets/js/vues/vues.js
index 8cc1dd656..31d833bfb 100644
--- a/resources/assets/js/vues/vues.js
+++ b/resources/assets/js/vues/vues.js
@@ -7,12 +7,15 @@ function exists(id) {
 let vueMapping = {
     'search-system': require('./search'),
     'entity-dashboard': require('./entity-search'),
+    'code-editor': require('./code-editor')
 };
 
+window.vues = {};
+
 Object.keys(vueMapping).forEach(id => {
     if (exists(id)) {
         let config = vueMapping[id];
         config.el = '#' + id;
-        new Vue(config);
+        window.vues[id] = new Vue(config);
     }
 });
\ No newline at end of file
diff --git a/resources/assets/sass/_codemirror.scss b/resources/assets/sass/_codemirror.scss
index 9f9e38f55..bd85218a5 100644
--- a/resources/assets/sass/_codemirror.scss
+++ b/resources/assets/sass/_codemirror.scss
@@ -248,6 +248,10 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
   -webkit-tap-highlight-color: transparent;
   -webkit-font-variant-ligatures: contextual;
   font-variant-ligatures: contextual;
+  &:after {
+    content: none;
+    display: none;
+  }
 }
 .CodeMirror-wrap pre {
   word-wrap: break-word;
diff --git a/resources/assets/sass/_components.scss b/resources/assets/sass/_components.scss
index 5328057d9..f45db84b7 100644
--- a/resources/assets/sass/_components.scss
+++ b/resources/assets/sass/_components.scss
@@ -466,4 +466,8 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group {
 
 .image-picker .none {
   display: none;
+}
+
+#code-editor .CodeMirror {
+  height: 400px;
 }
\ No newline at end of file
diff --git a/resources/assets/sass/_text.scss b/resources/assets/sass/_text.scss
index 4eaa492e7..ccef2a70f 100644
--- a/resources/assets/sass/_text.scss
+++ b/resources/assets/sass/_text.scss
@@ -135,6 +135,21 @@ pre {
   font-size: 12px;
   background-color: #f5f5f5;
   border: 1px solid #DDD;
+  padding-left: 31px;
+  position: relative;
+  padding-top: 3px;
+  padding-bottom: 3px;
+  &:after {
+    content: '';
+    display: block;
+    position: absolute;
+    top: 0;
+    width: 29px;
+    left: 0;
+    background-color: #f5f5f5;
+    height: 100%;
+    border-right: 1px solid #DDD;
+  }
 }
 
 
@@ -182,6 +197,7 @@ pre code {
   border: 0;
   font-size: 1em;
   display: block;
+  line-height: 1.6;
 }
 /*
  * Text colors
diff --git a/resources/lang/en/components.php b/resources/lang/en/components.php
index b9108702a..334502d05 100644
--- a/resources/lang/en/components.php
+++ b/resources/lang/en/components.php
@@ -20,5 +20,13 @@ return [
     'image_preview' => 'Image Preview',
     'image_upload_success' => 'Image uploaded successfully',
     'image_update_success' => 'Image details successfully updated',
-    'image_delete_success' => 'Image successfully deleted'
+    'image_delete_success' => 'Image successfully deleted',
+
+    /**
+     * Code editor
+     */
+    'code_editor' => 'Edit Code',
+    'code_language' => 'Code Language',
+    'code_content' => 'Code Content',
+    'code_save' => 'Save Code',
 ];
\ No newline at end of file
diff --git a/resources/views/components/code-editor.blade.php b/resources/views/components/code-editor.blade.php
new file mode 100644
index 000000000..23deaad99
--- /dev/null
+++ b/resources/views/components/code-editor.blade.php
@@ -0,0 +1,29 @@
+<div id="code-editor">
+    <div class="overlay" ref="overlay" v-cloak @click="hide()">
+        <div class="popup-body" @click.stop>
+
+            <div class="popup-header primary-background">
+                <div class="popup-title">{{ trans('components.code_editor') }}</div>
+                <button class="popup-close neg corner-button button" @click="hide()">x</button>
+            </div>
+
+            <div class="padded">
+                <div class="form-group">
+                    <label for="code-editor-language">{{ trans('components.code_language') }}</label>
+                    <input @keypress.enter="save()" id="code-editor-language" type="text" @input="updateEditorMode(language)" v-model="language">
+                </div>
+
+                <div class="form-group">
+                    <label for="code-editor-content">{{ trans('components.code_content') }}</label>
+                    <textarea ref="editor" v-model="code"></textarea>
+                </div>
+
+                <div class="form-group">
+                    <button type="button" class="button pos" @click="save()">{{ trans('components.code_save') }}</button>
+                </div>
+
+            </div>
+
+        </div>
+    </div>
+</div>
\ No newline at end of file
diff --git a/resources/views/pages/edit.blade.php b/resources/views/pages/edit.blade.php
index 5ab25d1cc..6de47aaf1 100644
--- a/resources/views/pages/edit.blade.php
+++ b/resources/views/pages/edit.blade.php
@@ -21,6 +21,7 @@
     </div>
     
     @include('components.image-manager', ['imageType' => 'gallery', 'uploaded_to' => $page->id])
+    @include('components.code-editor')
     @include('components.entity-selector-popup')
 
 @stop
\ No newline at end of file