diff --git a/pwnagotchi/plugins/__init__.py b/pwnagotchi/plugins/__init__.py
index 44c2787..c5efaf7 100644
--- a/pwnagotchi/plugins/__init__.py
+++ b/pwnagotchi/plugins/__init__.py
@@ -6,7 +6,7 @@ import logging
 
 default_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "default")
 loaded = {}
-
+database = {}
 
 class Plugin:
     @classmethod
@@ -18,6 +18,26 @@ class Plugin:
         logging.debug("loaded plugin %s as %s" % (plugin_name, plugin_instance))
         loaded[plugin_name] = plugin_instance
 
+def toggle_plugin(name, enable=True):
+    """
+    Load or unload a plugin
+
+    returns True if changed, otherwise False
+    """
+    global loaded, database
+    if not enable and name in loaded:
+        if getattr(loaded[name], 'on_unload', None):
+            loaded[name].on_unload()
+        del loaded[name]
+        return True
+
+    if enable and name in database and name not in loaded:
+        load_from_file(database[name])
+        one(name, 'loaded')
+        return True
+
+    return False
+
 
 def on(event_name, *args, **kwargs):
     for plugin_name, plugin in loaded.items():
@@ -48,10 +68,11 @@ def load_from_file(filename):
 
 
 def load_from_path(path, enabled=()):
-    global loaded
+    global loaded, database
     logging.debug("loading plugins from %s - enabled: %s" % (path, enabled))
     for filename in glob.glob(os.path.join(path, "*.py")):
         plugin_name = os.path.basename(filename.replace(".py", ""))
+        database[plugin_name] = filename
         if plugin_name in enabled:
             try:
                 load_from_file(filename)
diff --git a/pwnagotchi/plugins/default/bt-tether.py b/pwnagotchi/plugins/default/bt-tether.py
index 6acaf27..5f986f8 100644
--- a/pwnagotchi/plugins/default/bt-tether.py
+++ b/pwnagotchi/plugins/default/bt-tether.py
@@ -466,7 +466,11 @@ class BTTether(plugins.Plugin):
         logging.info("BT-TETHER: Successfully loaded ...")
         self.ready = True
 
+    def on_unload(self):
+        self.ui.remove_element('bluetooth')
+
     def on_ui_setup(self, ui):
+        self.ui = ui
         ui.add_element('bluetooth', LabeledValue(color=BLACK, label='BT', value='-', position=(ui.width() / 2 - 15, 0),
                                                  label_font=fonts.Bold, text_font=fonts.Medium))
 
diff --git a/pwnagotchi/plugins/default/example.py b/pwnagotchi/plugins/default/example.py
index 035a30d..37eccb4 100644
--- a/pwnagotchi/plugins/default/example.py
+++ b/pwnagotchi/plugins/default/example.py
@@ -25,6 +25,10 @@ class Example(plugins.Plugin):
     def on_loaded(self):
         logging.warning("WARNING: this plugin should be disabled! options = " % self.options)
 
+    # called before the plugin is unloaded
+    def on_unload(self):
+        pass
+
     # called hen there's internet connectivity
     def on_internet_available(self, agent):
         pass
diff --git a/pwnagotchi/ui/web/handler.py b/pwnagotchi/ui/web/handler.py
index c11b173..788a3d1 100644
--- a/pwnagotchi/ui/web/handler.py
+++ b/pwnagotchi/ui/web/handler.py
@@ -179,16 +179,19 @@ class Handler:
 
     def plugins(self, name, subpath):
         if name is None:
-            # show plugins overview
-            abort(404)
+            return render_template('plugins.html', loaded=plugins.loaded, database=plugins.database)
+
+        if name == 'toggle' and request.method == 'POST':
+            checked = True if 'enabled' in request.form else False
+            return 'success' if plugins.toggle_plugin(request.form['plugin'], checked) else 'failed'
+
+        if name in plugins.loaded and plugins.loaded[name] is not None and hasattr(plugins.loaded[name], 'on_webhook'):
+            try:
+                return plugins.loaded[name].on_webhook(subpath, request)
+            except Exception:
+                abort(500)
         else:
-            if name in plugins.loaded and hasattr(plugins.loaded[name], 'on_webhook'):
-                try:
-                    return plugins.loaded[name].on_webhook(subpath, request)
-                except Exception:
-                    abort(500)
-            else:
-                abort(404)
+            abort(404)
 
     # serve a message and shuts down the unit
     def shutdown(self):
diff --git a/pwnagotchi/ui/web/static/css/style.css b/pwnagotchi/ui/web/static/css/style.css
index 8acc267..a005a1d 100644
--- a/pwnagotchi/ui/web/static/css/style.css
+++ b/pwnagotchi/ui/web/static/css/style.css
@@ -31,4 +31,37 @@ a.read {
 
 p.messagebody {
     padding: 1em;
-}
\ No newline at end of file
+}
+
+li.navitem {
+    width: 16.66% !important;
+    clear: none !important;
+}
+
+/* Custom indentations are needed because the length of custom labels differs from
+   the length of the standard labels */
+.custom-size-flipswitch.ui-flipswitch .ui-btn.ui-flipswitch-on {
+    text-indent: -5.9em;
+}
+
+.custom-size-flipswitch.ui-flipswitch .ui-flipswitch-off {
+    text-indent: 0.5em;
+}
+
+/* Custom widths are needed because the length of custom labels differs from
+   the length of the standard labels */
+.custom-size-flipswitch.ui-flipswitch {
+    width: 8.875em;
+}
+
+.custom-size-flipswitch.ui-flipswitch.ui-flipswitch-active {
+    padding-left: 7em;
+    width: 1.875em;
+}
+
+@media (min-width: 28em) {
+    /*Repeated from rule .ui-flipswitch above*/
+    .ui-field-contain > label + .custom-size-flipswitch.ui-flipswitch {
+        width: 1.875em;
+    }
+}
diff --git a/pwnagotchi/ui/web/templates/base.html b/pwnagotchi/ui/web/templates/base.html
index c39e080..be2d618 100644
--- a/pwnagotchi/ui/web/templates/base.html
+++ b/pwnagotchi/ui/web/templates/base.html
@@ -47,6 +47,7 @@
         ( '/inbox/new', 'new', 'mail', 'New' ),
         ( '/inbox/profile', 'profile', 'info', 'Profile' ),
         ( '/inbox/peers', 'peers', 'user', 'Peers' ),
+        ( '/plugins', 'plugins', 'grid', 'Plugins' ),
     ] %}
     {% set active_page = active_page|default('inbox') %}
 
@@ -54,7 +55,7 @@
         <div data-role="navbar" data-iconpos="left">
             <ul>
                 {% for href, id, icon, caption in navigation %}
-                    <li>
+                    <li class="navitem">
                        <a href="{{ href }}" id="{{ id }}" data-icon="{{ icon }}" class="{{ 'ui-btn-active' if active_page == id }}">{{ caption }}</a>
                     </li>
                 {% endfor %}
diff --git a/pwnagotchi/ui/web/templates/plugins.html b/pwnagotchi/ui/web/templates/plugins.html
new file mode 100644
index 0000000..e96d244
--- /dev/null
+++ b/pwnagotchi/ui/web/templates/plugins.html
@@ -0,0 +1,39 @@
+{% extends "base.html" %}
+{% set active_page = "plugins" %}
+
+{% block title %}
+Plugins
+{% endblock %}
+
+{% block script %}
+$(function(){
+    $("input[type=checkbox]").change(function(e) {
+          var checkbox = $(this);
+          var form = checkbox.closest("form");
+          var url = form.attr('action');
+
+          $.ajax({
+             type: "POST",
+             url: url,
+             data: form.serialize(),
+             success: function(data) {
+                  if( data.indexOf('failed') != -1 ) {
+                      alert('Could not be toggled.');
+                  }
+            }
+        });
+    });
+});
+{% endblock %}
+{% block content %}
+<div style="padding: 1em">
+    {% for name in database.keys() %}
+      <h4>{{name}}</h4>
+      <form method="POST" action="/plugins/toggle">
+        <input type="checkbox" data-role="flipswitch" name="enabled" id="flip-checkbox-{{name}}" data-on-text="Enabled" data-off-text="Disabled" data-wrapper-class="custom-size-flipswitch" {% if name in loaded %} checked {% endif %}>
+        <input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
+        <input type="hidden" name="plugin" value="{{ name }}"/>
+      </form>
+    {% endfor %}
+</div>
+{% endblock %}