diff --git a/Changelog.md b/Changelog.md
index 51d5dad..a05d522 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -14,6 +14,10 @@ This file holds the changelog for Pepperminty Wiki. This is the master list of t
  - Added dark theme to the [downloader](https://starbeamrainbowlabs.com/labs/peppermint/download.php) (will be updated at the next stable release)
  - Added initial mobile theme support to the default theme
      - There's still a bunch of work to do in this department, but it's a bit of a challenge to do so without breaking desktop support
+ - Added autocomplete for tags when editing pages, powered by [Awesomplete](https://leaverou.github.io/awesomplete/)
+     - The new `editing_tags_autocomplete` setting - enabled by default - toggles it, but why would you want to turn it off? :P
+     - It should be reasonably accessible, judging from all the aria tags I'm seeing
+     - Get in touch if you experience performance issues with fetching tag lists from your wiki
 
 ### Fixed
  - Fixed weighted word support on search query analysis debug page
diff --git a/modules/page-edit.php b/modules/page-edit.php
index bb8f022..e3be22b 100644
--- a/modules/page-edit.php
+++ b/modules/page-edit.php
@@ -6,7 +6,9 @@ register_module([
 	"description" => "Allows you to edit pages by adding the edit and save actions. You should probably include this one.",
 	"id" => "page-edit",
 	"extra_data" => [
-		"diff.min.js" => "https://cdnjs.cloudflare.com/ajax/libs/jsdiff/2.2.2/diff.min.js"
+		"diff.min.js" => "https://cdnjs.cloudflare.com/ajax/libs/jsdiff/2.2.2/diff.min.js",
+		"awesomplete.min.js" => "https://cdnjs.cloudflare.com/ajax/libs/awesomplete/1.1.5/awesomplete.min.js",
+		"awesomplete.min.css" => "https://cdnjs.cloudflare.com/ajax/libs/awesomplete/1.1.5/awesomplete.min.css"
 	],
 	
 	"code" => function() {
@@ -33,7 +35,7 @@ register_module([
 		 *             %edit%
 		 */
 		add_action("edit", function() {
-			global $pageindex, $settings, $env;
+			global $pageindex, $settings, $env, $paths;
 			
 			$filename = "$env->storage_prefix$env->page.md";
 			$creatingpage = !isset($pageindex->{$env->page});
@@ -194,6 +196,41 @@ window.addEventListener("load", function(event) {
 		smartsave_restore();
 	});
 });');
+			// Why would anyone want this disabled?
+			if($settings->editing_tags_autocomplete) {
+				page_renderer::add_js_link("$paths->extra_data_directory/page-edit/awesomplete.min.js");
+				page_renderer::add_js_snippet('window.addEventListener("load", async (event) => {
+	// FUTURE: Optionally cache this?
+	let response = await fetch("?action=list-tags&format=text");
+	if(!response.ok) {
+		console.warn(`Warning: Failed to fetch tags list with status code ${response.status} ${response.statusText}`);
+		return;
+	}
+	
+	let tags = (await response.text()).split("\n");
+	console.log(tags);
+	window.input_tags_completer = new Awesomplete(
+		document.querySelector("#tags"), {
+			list: tags,
+			filter: function(text, input) {
+				console.log(arguments);
+				// Avoid suggesting tags that are already present
+				if(input.split(/,\s*/).includes(text.value)) return false;
+				return Awesomplete.FILTER_CONTAINS(text, input.match(/[^,]*$/)[0]);
+			},
+			item: function(text, input) {
+				return Awesomplete.ITEM(text, input.match(/[^,]*$/)[0]);
+			},
+			replace: function(text) {
+				var before = this.input.value.match(/^.+,\s*|/)[0];
+				this.input.value = before + text + ", ";
+			}
+		}
+	);
+});
+				');
+				$content .= "<link rel=\"stylesheet\" href=\"$paths->extra_data_directory/page-edit/awesomplete.min.css\" />";
+			}
 			
 			exit(page_renderer::render_main("$title - $settings->sitename", $content));
 		});
@@ -413,7 +450,7 @@ window.addEventListener("load", function(event) {
 					</form>";
 					
 					// Insert a reference to jsdiff to generate the diffs
-					$diffScript = <<<'DIFFSCRIPT'
+					$diff_script = <<<'DIFFSCRIPT'
 window.addEventListener("load", function(event) {
 	var destination = document.getElementById("highlighted-diff"),
 	diff = JsDiff.diffWords(document.getElementById("original-content").value, document.getElementById("new-content").value),
@@ -427,9 +464,9 @@ window.addEventListener("load", function(event) {
 	destination.innerHTML = output;
 });
 DIFFSCRIPT;
-					// diff.min.js is downloaded above
-					$content .= "\n<script src='$paths->extra_data_dir/page-edit/diff.min.js'></script>
-					<script>$diffScript</script>\n";
+
+					page_renderer::add_js_link("$paths->extra_data_directory/page-edit/diff.min.js");
+					page_renderer::add_js_snippet($diff_script);
 					
 					header("x-failure-reason: edit-conflict");
 					exit(page_renderer::render_main("Edit Conflict - $env->page - $settings->sitename", $content));
diff --git a/peppermint.guiconfig.json b/peppermint.guiconfig.json
index 018331f..5606032 100644
--- a/peppermint.guiconfig.json
+++ b/peppermint.guiconfig.json
@@ -13,6 +13,7 @@
 	"random_page_exclude_redirects": { "type": "checkbox", "description": "Causes the random action to avoid sending the user to a redirect page.", "default": true },
 	"footer_message": { "type": "textarea", "description": "A message that will appear at the bottom of every page. May contain HTML.", "default": "All content is under <a href='?page=License' target='_blank'>this license</a>. Please make sure that you read and understand the license, especially if you are thinking about copying some (or all) of this site's content, as it may restrict you from doing so." },
 	"editing_message": { "type": "textarea", "description": "A message that will appear just before the submit button on the editing page. May contain HTML.", "default": "<a href='?action=help#20-parser-default' target='_blank'>Formatting help</a> (<a href='https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet' target='_blank'>Markdown Cheatsheet</a>)<br />\nBy submitting your edit or uploading your file, you are agreeing to release your changes under <a href='?action=view&page=License' target='_blank'>this license</a>. Also note that if you don't want your work to be edited by other users of this site, please don't submit it here!" },
+	"editing_tags_autocomplete": { "type": "checkbox", "description": "Whether to enable autocomplete for the tags box in the page editor.", "default": true },
 	"admindisplaychar": { "type": "text", "description": "The string that is prepended before an admin's name on the nav bar. Defaults to a diamond shape (&#9670;).", "default": "&#9670;" },
 	"protectedpagechar": { "type": "text", "description": "The string that is prepended a page's name in the page title if it is protected. Defaults to a lock symbol. (&#128274;)", "default": "&#128274;" },
 	"editing": { "type": "checkbox", "description": "Whether editing is enabled.", "default": true},
diff --git a/themes/blue/theme.css b/themes/blue/theme.css
index e516159..5189203 100644
--- a/themes/blue/theme.css
+++ b/themes/blue/theme.css
@@ -283,6 +283,7 @@ main label:not(.link-display-label){
 	display:inline-block;
 	min-width:16rem;
 }
+.awesomplete { width: 100%; }
 input[type=text]:not(.link-display), input[type=password], input[type=url], input[type=email], input[type=number], textarea{
 	margin:0.5rem 0;
 }
diff --git a/themes/default/theme.css b/themes/default/theme.css
index 52fe9aa..479cfc3 100644
--- a/themes/default/theme.css
+++ b/themes/default/theme.css
@@ -196,6 +196,13 @@ a.interwiki_link:active { color: var(--accent-d3); }
 .editform { position: relative; }
 textarea[name=content] { resize: none; }
 .fit-text-mirror { position: absolute; top: 0; left: -10000vw; word-wrap: break-word; white-space: pre-wrap; }
+
+.awesomplete { width: 100%; color: var(--text-dark); }
+.awesomplete > ul::before { display: none; }
+/* Overly specific to override library css */
+.awesomplete > ul[role=listbox] { top: 2.5em; background: var(--accent-a2); }
+
+
 main label:not(.link-display-label) { display: inline-block; min-width: 16rem; }
 input[type=text]:not(.link-display), input[type=password], input[type=url], input[type=email], input[type=number], textarea { margin: 0.5rem 0; }
 input[type=text], input[type=password], input[type=url], input[type=email], input[type=number], textarea, textarea[name=content] + pre, #search-box { padding: 0.5rem 0.8rem; background: var(--accent-a4); border: 0; border-radius: 0.3rem; font-size: 1rem; color: var(--text-bright); }
diff --git a/themes/photo/theme.css b/themes/photo/theme.css
index 0ae767d..d574aa3 100644
--- a/themes/photo/theme.css
+++ b/themes/photo/theme.css
@@ -121,6 +121,7 @@ textarea, #tags, #search-box {
 	
 }
 .fit-text-mirror { position: absolute; left: -1000000px; }
+.awesomplete { width: 100%; }
 
 textarea, input:not([type=submit]):not([type=button]) {
 	font-family: "Ubuntu", sans-serif;
