diff --git a/_embed/public/js/common.js b/_embed/public/js/common.js
index 8bfea4ae..d4e1a56f 100644
--- a/_embed/public/js/common.js
+++ b/_embed/public/js/common.js
@@ -266,7 +266,7 @@ function openEvent(event) {
   if(selectedItems.length) {
     link = document.getElementById(selectedItems[0]).dataset.url + link;
   } else {
-    link = window.location + link;
+    link = window.location.pathname + link;
   }
 
   window.open(link);
diff --git a/_embed/public/js/editor.js b/_embed/public/js/editor.js
index a5217a0f..bcaf19fc 100644
--- a/_embed/public/js/editor.js
+++ b/_embed/public/js/editor.js
@@ -22,6 +22,17 @@ editor.textareaAutoGrow = function () {
   window.addEventListener('resize', addAutoGrow)
 }
 
+editor.toggleSourceEditor = function (event) {
+  event.preventDefault();
+
+  if(document.querySelector('[data-kind="content-only"]')) {
+    window.location = window.location.pathname + "?visual=true"
+    return;
+  }
+
+  window.location = window.location.pathname + "?visual=false"
+}
+
 function deleteFrontMatterItem(event) {
   event.preventDefault();
   document.getElementById(this.dataset.delete).remove();
@@ -163,11 +174,18 @@ document.addEventListener("DOMContentLoaded", (event) => {
   if(!document.getElementById('editor')) return;
 
   editor.textareaAutoGrow();
+
   templates.arrayItem = document.getElementById("array-item-template");
   templates.base = document.getElementById('base-template');
   templates.objectItem = document.getElementById("object-item-template");
   templates.temporary = document.getElementById('temporary-template');
+
   buttons.save = document.querySelector('#save');
+  buttons.editSource = document.querySelector('#edit-source');
+
+  if(buttons.editSource) {
+    buttons.editSource.addEventListener('click', editor.toggleSourceEditor)
+  }
 
   let container = document.getElementById('editor'),
     kind = container.dataset.kind;
@@ -255,4 +273,4 @@ document.addEventListener("DOMContentLoaded", (event) => {
   });
 
   return false;
-});
\ No newline at end of file
+});
diff --git a/_embed/templates/base.tmpl b/_embed/templates/base.tmpl
index 6c571f0e..9e357acf 100644
--- a/_embed/templates/base.tmpl
+++ b/_embed/templates/base.tmpl
@@ -67,9 +67,16 @@
             <div class="actions{{ if .IsDir }} disabled{{ end }}" id="file-only">
                 {{- if and (not .IsDir) (.User.AllowEdit) }}
                 {{- if .Editor}}
+
                 {{- if eq .Data.Mode "markdown" }}
                 <div class="action" id="preview" onclick="notImplemented(event);">
-                    <i class="material-icons">remove_red_eye</i>
+                    <i class="material-icons" title="Preview">remove_red_eye</i>
+                </div>
+                {{- end }}
+
+                {{- if eq .Data.Visual true }}
+                <div class="action" id="edit-source">
+                    <i class="material-icons" title="Toggle edit source">code</i>
                 </div>
                 {{- end }}
                 {{- end }}
diff --git a/handlers/editor.go b/handlers/editor.go
index 0d827b55..0726ace2 100644
--- a/handlers/editor.go
+++ b/handlers/editor.go
@@ -2,7 +2,8 @@ package handlers
 
 import (
 	"bytes"
-	"fmt"
+	"errors"
+	"net/http"
 	"path/filepath"
 	"strings"
 
@@ -15,78 +16,91 @@ import (
 type Editor struct {
 	Class       string
 	Mode        string
+	Visual      bool
 	Content     string
 	FrontMatter *frontmatter.Content
 }
 
 // GetEditor gets the editor based on a FileInfo struct
-func GetEditor(i *file.Info) (*Editor, error) {
-	// Create a new editor variable and set the mode
-	editor := new(Editor)
-	editor.Mode = strings.TrimPrefix(filepath.Ext(i.Name), ".")
-
-	switch editor.Mode {
-	case "md", "markdown", "mdown", "mmark":
-		editor.Mode = "markdown"
-	case "asciidoc", "adoc", "ad":
-		editor.Mode = "asciidoc"
-	case "rst":
-		editor.Mode = "rst"
-	case "html", "htm":
-		editor.Mode = "html"
-	case "js":
-		editor.Mode = "javascript"
-	case "go":
-		editor.Mode = "golang"
-	}
-
-	var page parser.Page
+func GetEditor(r *http.Request, i *file.Info) (*Editor, error) {
 	var err error
 
-	// Handle the content depending on the file extension
-	switch editor.Mode {
-	case "json", "toml", "yaml":
-		// Defines the class and declares an error
-		editor.Class = "frontmatter-only"
+	// Create a new editor variable and set the mode
+	editor := new(Editor)
+	editor.Mode = editorMode(i.Name)
+	editor.Class = editorClass(editor.Mode)
 
+	if editor.Class == "frontmatter-only" || editor.Class == "complete" {
+		editor.Visual = true
+	}
+
+	if r.URL.Query().Get("visual") == "false" {
+		editor.Class = "content-only"
+	}
+
+	if editor.Class == "frontmatter-only" {
 		// Checks if the file already has the frontmatter rune and parses it
 		if frontmatter.HasRune(i.Content) {
 			editor.FrontMatter, _, err = frontmatter.Pretty(i.Content)
 		} else {
 			editor.FrontMatter, _, err = frontmatter.Pretty(frontmatter.AppendRune(i.Content, editor.Mode))
 		}
+	}
+
+	if editor.Class == "complete" && frontmatter.HasRune(i.Content) {
+		var page parser.Page
+		// Starts a new buffer and parses the file using Hugo's functions
+		buffer := bytes.NewBuffer(i.Content)
+		page, err = parser.ReadFrom(buffer)
+		editor.Class = "complete"
 
-		// Check if there were any errors
 		if err == nil {
-			break
+			// Parses the page content and the frontmatter
+			editor.Content = strings.TrimSpace(string(page.Content()))
+			editor.FrontMatter, _, err = frontmatter.Pretty(page.FrontMatter())
 		}
+	}
 
-		fmt.Println("Hey")
+	if editor.Class == "complete" && !frontmatter.HasRune(i.Content) {
+		err = errors.New("Complete but without rune")
+	}
 
-		fallthrough
-	case "markdown", "asciidoc", "rst":
-		if frontmatter.HasRune(i.Content) {
-			// Starts a new buffer and parses the file using Hugo's functions
-			buffer := bytes.NewBuffer(i.Content)
-			page, err = parser.ReadFrom(buffer)
-			editor.Class = "complete"
-
-			if err == nil {
-				// Parses the page content and the frontmatter
-				editor.Content = strings.TrimSpace(string(page.Content()))
-				editor.FrontMatter, _, err = frontmatter.Pretty(page.FrontMatter())
-
-				if err == nil {
-					break
-				}
-			}
-		}
-
-		fallthrough
-	default:
+	if editor.Class == "content-only" || err != nil {
 		editor.Class = "content-only"
 		editor.Content = i.StringifyContent()
 	}
 
 	return editor, nil
 }
+
+func editorClass(mode string) string {
+	switch mode {
+	case "json", "toml", "yaml":
+		return "frontmatter-only"
+	case "markdown", "asciidoc", "rst":
+		return "complete"
+	}
+
+	return "content-only"
+}
+
+func editorMode(filename string) string {
+	mode := strings.TrimPrefix(filepath.Ext(filename), ".")
+
+	switch mode {
+	case "md", "markdown", "mdown", "mmark":
+		mode = "markdown"
+	case "asciidoc", "adoc", "ad":
+		mode = "asciidoc"
+	case "rst":
+		mode = "rst"
+	case "html", "htm":
+		mode = "html"
+	case "js":
+		mode = "javascript"
+	case "go":
+		mode = "golang"
+	}
+
+	return mode
+}
diff --git a/handlers/single.go b/handlers/single.go
index e6e6877d..5a9b0eb7 100644
--- a/handlers/single.go
+++ b/handlers/single.go
@@ -36,7 +36,7 @@ func ServeSingle(w http.ResponseWriter, r *http.Request, c *config.Config, u *co
 	}
 
 	if i.CanBeEdited() && u.AllowEdit {
-		p.Data, err = GetEditor(i)
+		p.Data, err = GetEditor(r, i)
 		p.Editor = true
 		if err != nil {
 			return http.StatusInternalServerError, err