diff --git a/gui/gui.qrc b/gui/gui.qrc
index be0d3e9909fa29f51fcbdf4022ac915d4847a051..30997279d123022755d37f067dec238279192305 100644
--- a/gui/gui.qrc
+++ b/gui/gui.qrc
@@ -8,6 +8,12 @@
         <file>qml/LoginDialog.qml</file>
         <file>qml/LoginOKDialog.qml</file>
         <file>qml/FailDialog.qml</file>
+        <file>qml/MainBar.qml</file>
+        <file>qml/BackgroundImage.qml</file>
+        <file>qml/LocationText.qml</file>
+        <file>qml/VPNSwitch.qml</file>
+        <file>qml/BridgesItem.qml</file>
+        <file>qml/logic.js</file>
 
         <file>assets/icon/png/black/vpn_off.png</file>
         <file>assets/icon/png/black/vpn_on.png</file>
diff --git a/gui/qml/BackgroundImage.qml b/gui/qml/BackgroundImage.qml
new file mode 100644
index 0000000000000000000000000000000000000000..86b8cba766ba044155b211e0ebfbba77bfdd9a5a
--- /dev/null
+++ b/gui/qml/BackgroundImage.qml
@@ -0,0 +1,15 @@
+import QtQuick 2.9
+import QtQuick.Controls 2.4
+
+Rectangle {
+
+    anchors.fill: parent;
+    anchors.topMargin: 40;
+
+    Image {
+        source: "qrc:/assets/img/bird.jpg";
+        fillMode: Image.PreserveAspectCrop;
+        anchors.fill: parent; 
+        opacity: 0.8;
+    }
+}
diff --git a/gui/qml/BridgesItem.qml b/gui/qml/BridgesItem.qml
new file mode 100644
index 0000000000000000000000000000000000000000..1cf152c026d739e931bd753dad24954978ee05b4
--- /dev/null
+++ b/gui/qml/BridgesItem.qml
@@ -0,0 +1,58 @@
+import QtQuick 2.9
+import QtQuick.Layouts 1.12
+import QtQuick.Controls 2.4
+
+import "logic.js" as Logic
+
+Item {
+
+    anchors.centerIn: parent
+    width: parent.width
+    property alias displayReconnect: bridgeReconnect.visible
+
+    Column {
+
+        anchors.centerIn: parent
+        spacing: 10
+        width: parent.width
+
+        CheckBox {
+            id: bridgeCheck
+            checked: false
+            text: qsTr("Use obfs4 bridges")
+            font.pixelSize: 14
+            anchors.horizontalCenter: parent.horizontalCenter
+            onClicked: {
+                if (checked) {
+                    Logic.setNeedsReconnect(true);
+                    bridgeReconnect.visible = true;
+                } else {
+                    // This would also need a "needs reconnect" for de-selecting bridges the next time.
+                    // better to wait and see the new connection widgets though
+                    Logic.setNeedsReconnect(false);
+                    bridgeReconnect.visible = false;
+                }
+            }
+        }
+
+        Text {
+            id: bridgesInfo
+            width: 250
+            color: "grey"
+            text: qsTr("Select a bridge only if you know that you need it to evade censorship in your country or local network.")
+            anchors.horizontalCenter: parent.horizontalCenter
+            wrapMode: Text.WordWrap 
+        }
+
+        Text {
+            id: bridgeReconnect
+            width: 250
+            font.pixelSize: 12
+            color: "red"
+            text: qsTr("We will attempt to connect to a bridge the next time you connect to the VPN.")
+            anchors.horizontalCenter: parent.horizontalCenter
+            wrapMode: Text.WordWrap 
+            visible: false;
+        }
+    }
+}
diff --git a/gui/qml/LocationText.qml b/gui/qml/LocationText.qml
new file mode 100644
index 0000000000000000000000000000000000000000..883a8a4a3552e8bc8b72ba53745888ef9840642a
--- /dev/null
+++ b/gui/qml/LocationText.qml
@@ -0,0 +1,8 @@
+import QtQuick 2.9
+
+Text {
+    font.pixelSize: 10
+    color: "black"
+    text: qsTr("Location has been manually set.")
+    anchors.horizontalCenter: parent.horizontalCenter
+}
diff --git a/gui/qml/MainBar.qml b/gui/qml/MainBar.qml
new file mode 100644
index 0000000000000000000000000000000000000000..1f28bace582c066dc5e3ea0bb40b6c3a4e94f79b
--- /dev/null
+++ b/gui/qml/MainBar.qml
@@ -0,0 +1,16 @@
+import QtQuick 2.9
+import QtQuick.Controls 2.4
+
+TabBar {
+    //id: bar
+    width: parent.width
+    TabButton {
+        text: qsTr("Status")
+    }
+    TabButton {
+        text: qsTr("Location")
+    }
+    TabButton {
+        text: qsTr("Bridges")
+    }
+}
diff --git a/gui/qml/VPNSwitch.qml b/gui/qml/VPNSwitch.qml
new file mode 100644
index 0000000000000000000000000000000000000000..14b37347f0a0bbaa475a8cbab60e9130366acddb
--- /dev/null
+++ b/gui/qml/VPNSwitch.qml
@@ -0,0 +1,64 @@
+import QtQuick 2.9
+import QtQuick.Layouts 1.12
+import QtQuick.Controls 2.4
+
+
+SwitchDelegate {
+
+    //id: vpntoggle
+
+    text: qsTr("")
+    checked: false
+    anchors.horizontalCenter: parent.horizontalCenter
+
+    /*
+    Connections {
+        function onCheckedChanged() {
+            if (vpntoggle.checked == true
+                    && ctx.status == "off") {
+                backend.switchOn()
+            }
+            if (vpntoggle.checked === false
+                    && ctx.status == "on") {
+                backend.switchOff()
+            }
+        }
+    }
+    */
+
+    contentItem: Text {
+        rightPadding: vpntoggle.indicator.width + vpntoggle.spacing
+        text: vpntoggle.text
+        font: vpntoggle.font
+        opacity: enabled ? 1.0 : 0.5
+        color: vpntoggle.down ? "#17a81a" : "#21be2b"
+        elide: Text.ElideRight
+        verticalAlignment: Text.AlignVCenter
+    }
+
+    indicator: Rectangle {
+        implicitWidth: 48
+        implicitHeight: 26
+        x: vpntoggle.width - width - vpntoggle.rightPadding
+        y: parent.height / 2 - height / 2
+        radius: 13
+        color: vpntoggle.checked ? "#17a81a" : "transparent"
+        border.color: vpntoggle.checked ? "#17a81a" : "#cccccc"
+
+        Rectangle {
+            x: vpntoggle.checked ? parent.width - width : 0
+            width: 26
+            height: 26
+            radius: 13
+            color: vpntoggle.down ? "#cccccc" : "#ffffff"
+            border.color: vpntoggle.checked ? (vpntoggle.down ? "#17a81a" : "#21be2b") : "#999999"
+        }
+    }
+
+    background: Rectangle {
+        implicitWidth: 100
+        implicitHeight: 40
+        visible: vpntoggle.down || vpntoggle.highlighted
+        color: vpntoggle.down ? "#17a81a" : "#eeeeee"
+    }
+} // end switchdelegate
diff --git a/gui/qml/VpnState.qml b/gui/qml/VpnState.qml
index e1a183cf4812973439686c4265ad6c0139c9ea8c..332dcaafe22c941af7fe49e61cb899f342bb8e2d 100644
--- a/gui/qml/VpnState.qml
+++ b/gui/qml/VpnState.qml
@@ -1,6 +1,8 @@
 import QtQuick 2.0
 import QtQuick.Controls 2.12
 
+import "logic.js" as Logic
+
 StateGroup {
 
     state: ctx ? ctx.status : ""
@@ -11,9 +13,12 @@ StateGroup {
         },
         State {
             name: "off"
+            StateChangeScript {
+                script: Logic.setStatus("off");
+            }
             PropertyChanges {
                 target: systray
-                tooltip: toHuman("off")
+                tooltip: Logic.toHuman("off")
                 icon.source: icons["off"]
             }
             PropertyChanges {
@@ -22,15 +27,15 @@ StateGroup {
             }
             PropertyChanges {
                 target: statusItem
-                text: toHuman("off")
+                text: Logic.toHuman("off")
             }
             PropertyChanges {
                 target: autoSelectionItem
-                text: qsTr("Best")
+                text: qsTr("Recommended")
             }
             PropertyChanges {
                 target: mainStatus
-                text: toHuman("off")
+                text: Logic.toHuman("off")
             }
             PropertyChanges {
                 target: mainCurrentGateway
@@ -39,9 +44,16 @@ StateGroup {
         },
         State {
             name: "on"
+            StateChangeScript {
+                script: {
+                    Logic.setNeedsReconnect(false);
+                    brReconnect = false;
+                }
+
+            }
             PropertyChanges {
                 target: systray
-                tooltip: toHuman("on")
+                tooltip: Logic.toHuman("on")
                 icon.source: icons["on"]
             }
             PropertyChanges {
@@ -50,7 +62,7 @@ StateGroup {
             }
             PropertyChanges {
                 target: statusItem
-                text: toHuman("on")
+                text: Logic.toHuman("on")
             }
             PropertyChanges {
                 target: autoSelectionItem
@@ -65,7 +77,7 @@ StateGroup {
             }
             PropertyChanges {
                 target: mainStatus
-                text: toHuman("on")
+                text: Logic.toHuman("on")
             }
             PropertyChanges {
                 target: mainCurrentGateway
@@ -77,12 +89,12 @@ StateGroup {
             name: "starting"
             PropertyChanges {
                 target: systray
-                tooltip: toHuman("connecting")
+                tooltip: Logic.toHuman("connecting")
                 icon.source: icons["wait"]
             }
             PropertyChanges {
                 target: statusItem
-                text: toHuman("connecting")
+                text: Logic.toHuman("connecting")
             }
             PropertyChanges {
                 target: autoSelectionItem
@@ -108,12 +120,12 @@ StateGroup {
             name: "stopping"
             PropertyChanges {
                 target: systray
-                tooltip: toHuman("stopping")
+                tooltip: Logic.toHuman("stopping")
                 icon.source: icons["wait"]
             }
             PropertyChanges {
                 target: statusItem
-                text: toHuman("stopping")
+                text: Logic.toHuman("stopping")
             }
             PropertyChanges {
                 target: autoSelectionItem
@@ -121,7 +133,7 @@ StateGroup {
             }
             PropertyChanges {
                 target: mainStatus
-                text: toHuman("stopping")
+                text: Logic.toHuman("stopping")
             }
             PropertyChanges {
                 target: mainCurrentGateway
@@ -132,12 +144,12 @@ StateGroup {
             name: "failed"
             PropertyChanges {
                 target: systray
-                tooltip: toHuman("failed")
+                tooltip: Logic.toHuman("failed")
                 icon.source: icons["wait"]
             }
             PropertyChanges {
                 target: statusItem
-                text: toHuman("failed")
+                text: Logic.toHuman("failed")
             }
             PropertyChanges {
                 target: autoSelectionItem
@@ -145,7 +157,7 @@ StateGroup {
             }
             PropertyChanges {
                 target: mainStatus
-                text: toHuman("failed")
+                text: Logic.toHuman("failed")
             }
             PropertyChanges {
                 target: mainCurrentGateway
diff --git a/gui/qml/logic.js b/gui/qml/logic.js
new file mode 100644
index 0000000000000000000000000000000000000000..4fdbb995231893f56e7ded6bfae4bcf58288ae69
--- /dev/null
+++ b/gui/qml/logic.js
@@ -0,0 +1,39 @@
+let status = 'off';
+let needsReconnect = false;
+
+function setStatus(st) {
+    status = st;
+}
+
+function getStatus() { 
+    return status;
+}
+
+function setNeedsReconnect(val) {
+    needsReconnect = val;
+}
+
+function getNeedsReconnect() {
+    return needsReconnect;
+}
+
+function toHuman(st) {
+    switch (st) {
+    case "off":
+        //: %1 -> application name
+        return qsTr("%1 off").arg(ctx.appName)
+    case "on":
+        //: %1 -> application name
+        return qsTr("%1 on").arg(ctx.appName)
+    case "connecting":
+        //: %1 -> application name
+        return qsTr("Connecting to %1").arg(ctx.appName)
+    case "stopping":
+        //: %1 -> application name
+        return qsTr("Stopping %1").arg(ctx.appName)
+    case "failed":
+        //: %1 -> application name
+        return qsTr("%1 blocking internet").arg(
+                    ctx.appName) // TODO failed is not handed yet
+    }
+}
diff --git a/gui/qml/main.qml b/gui/qml/main.qml
index 7048a8108d2f33cc68d639495e0cd6b5755d5b9b..f75a121a20d014d3abb59a9473394b4116c65d19 100644
--- a/gui/qml/main.qml
+++ b/gui/qml/main.qml
@@ -5,6 +5,8 @@ import QtQuick.Controls 2.4
 
 import Qt.labs.platform 1.0
 
+import "logic.js" as Logic
+
 ApplicationWindow {
     id: app
     visible: true
@@ -18,26 +20,16 @@ ApplicationWindow {
     property var ctx
     property var loginDone
     property var allowEmptyPass
+    property var needsRestart
+
 
     onSceneGraphError: function(error, msg) {
         console.debug("ERROR while initializing scene")
         console.debug(msg)
     }
 
-    // TODO refactor into discrete components.
-
-    TabBar {
+    MainBar {
         id: bar
-        width: parent.width
-        TabButton {
-            text: qsTr("Status")
-        }
-        TabButton {
-            text: qsTr("Location")
-        }
-        TabButton {
-            text: qsTr("Bridges")
-        }
     }
 
     StackLayout {
@@ -50,17 +42,8 @@ ApplicationWindow {
             id: infoTab
             anchors.centerIn: parent
 
-            Rectangle {
+            BackgroundImage {
                 id: background
-                anchors.fill: parent;
-                anchors.topMargin: 40;
-
-                Image {
-                    source: "qrc:/assets/img/bird.jpg";
-                    fillMode: Image.PreserveAspectCrop;
-                    anchors.fill: parent; 
-                    opacity: 0.8;
-                }
             }
 
             Item {
@@ -96,14 +79,9 @@ ApplicationWindow {
                         anchors.horizontalCenter: parent.horizontalCenter
                     }
 
-                    SwitchDelegate {
-
+                    VPNSwitch {
                         id: vpntoggle
 
-                        text: qsTr("")
-                        checked: false
-                        anchors.horizontalCenter: parent.horizontalCenter
-
                         Connections {
                             function onCheckedChanged() {
                                 if (vpntoggle.checked == true
@@ -116,50 +94,10 @@ ApplicationWindow {
                                 }
                             }
                         }
+                    }
 
-                        contentItem: Text {
-                            rightPadding: vpntoggle.indicator.width + vpntoggle.spacing
-                            text: vpntoggle.text
-                            font: vpntoggle.font
-                            opacity: enabled ? 1.0 : 0.5
-                            color: vpntoggle.down ? "#17a81a" : "#21be2b"
-                            elide: Text.ElideRight
-                            verticalAlignment: Text.AlignVCenter
-                        }
-
-                        indicator: Rectangle {
-                            implicitWidth: 48
-                            implicitHeight: 26
-                            x: vpntoggle.width - width - vpntoggle.rightPadding
-                            y: parent.height / 2 - height / 2
-                            radius: 13
-                            color: vpntoggle.checked ? "#17a81a" : "transparent"
-                            border.color: vpntoggle.checked ? "#17a81a" : "#cccccc"
-
-                            Rectangle {
-                                x: vpntoggle.checked ? parent.width - width : 0
-                                width: 26
-                                height: 26
-                                radius: 13
-                                color: vpntoggle.down ? "#cccccc" : "#ffffff"
-                                border.color: vpntoggle.checked ? (vpntoggle.down ? "#17a81a" : "#21be2b") : "#999999"
-                            }
-                        }
-
-                        background: Rectangle {
-                            implicitWidth: 100
-                            implicitHeight: 40
-                            visible: vpntoggle.down || vpntoggle.highlighted
-                            color: vpntoggle.down ? "#17a81a" : "#eeeeee"
-                        }
-                    } // end switchdelegate
-
-                    Text {
+                    LocationText {
                         id: manualOverrideWarning
-                        font.pixelSize: 10
-                        color: "grey"
-                        text: qsTr("Location has been manually set.")
-                        anchors.horizontalCenter: parent.horizontalCenter
                         visible: isManualLocation()
                     }
                 } // end column
@@ -200,7 +138,6 @@ ApplicationWindow {
                         backend.useLocation(currentText.toString())
                     }
 
-
                     delegate: ItemDelegate {
                         // TODO: we could use icons
                         // https://doc.qt.io/qt-5/qml-qtquick-controls2-abstractbutton.html#icon-prop
@@ -232,44 +169,10 @@ ApplicationWindow {
             } // end column
         } // end item
 
-        Item {
-
+        BridgesItem {
             id: bridgesTab
-            anchors.centerIn: parent
-            width: parent.width
-
-            Column {
-
-                anchors.centerIn: parent
-                spacing: 10
-                width: parent.width
+        }
 
-                CheckBox {
-                    checked: false
-                    text: qsTr("Use obfs4 bridges")
-                    font.pixelSize: 14
-                    anchors.horizontalCenter: parent.horizontalCenter
-                }
-                Text {
-                    id: bridgesInfo
-                    width: 250
-                    color: "grey"
-                    text: qsTr("Select a bridge only if you know that you need it to evade censorship in your country or local network.")
-                    anchors.horizontalCenter: parent.horizontalCenter
-                    wrapMode: Text.WordWrap 
-                }
-                Text {
-                    id: bridgeReconnect
-                    width: 250
-                    font.pixelSize: 12
-                    color: "red"
-                    text: qsTr("The change will take effect next time you connect to the VPN.")
-                    anchors.horizontalCenter: parent.horizontalCenter
-                    wrapMode: Text.WordWrap 
-                    visible: true
-                }
-            } // end column
-        } // end item
     } // end stacklayout
 
     Connections {
@@ -374,36 +277,13 @@ ApplicationWindow {
         console.debug("DEBUG: Pre-seeded providers:")
         console.debug(providers.getJson())
         allowEmptyPass = shouldAllowEmptyPass()
+        needsRestart = false;
 
         /* TODO get appVisible flag from backend */
         app.visible = true
         app.raise()
     }
 
-    function toHuman(st) {
-        switch (st) {
-        case "off":
-            //: %1 -> application name
-            return qsTr("%1 off").arg(ctx.appName)
-        case "on":
-            //: %1 -> application name
-            return qsTr("%1 on").arg(ctx.appName)
-        case "connecting":
-            //: %1 -> application name
-            return qsTr("Connecting to %1").arg(ctx.appName)
-        case "stopping":
-            //: %1 -> application name
-            return qsTr("Stopping %1").arg(ctx.appName)
-        case "failed":
-            //: %1 -> application name
-            return qsTr("%1 blocking internet").arg(
-                        ctx.appName) // TODO failed is not handed yet
-        }
-    }
-
-    function locationStr() {
-        return ctx.currentLocation + ", " + ctx.currentCountry
-    }
 
     property var icons: {
         "off": "qrc:/assets/icon/png/white/vpn_off.png",
@@ -557,10 +437,8 @@ ApplicationWindow {
             systray.icon.source = icons["off"]
             tooltip = qsTr("Checking status…")
             console.debug("systray init completed")
-            //hide()
             if (systrayVisible) {
                 console.log("show systray")
-                //show()
                 if (Qt.platform.os === "windows") {
                     let appname = ctx ? ctx.appName : "VPN"
                     showNotification(
@@ -570,6 +448,7 @@ ApplicationWindow {
             }
         }
 
+        // TODO move to logic, pass ctx
         // Helper to show notification messages
         function showNotification(msg) {
             console.log("Going to show notification message: ", msg)
@@ -604,7 +483,6 @@ ApplicationWindow {
 
     MessageDialog {
         id: errorStartingVPN
-        //buttons: MessageDialog.Ok
         modality: Qt.NonModal
         title: qsTr("Error starting VPN")
         text: ""
@@ -614,7 +492,6 @@ ApplicationWindow {
 
     MessageDialog {
         id: authAgent
-        //buttons: MessageDialog.Ok
         modality: Qt.NonModal
         title: qsTr("Missing authentication agent")
         text: qsTr("Could not find a polkit authentication agent. Please run one and try again.")
@@ -625,4 +502,11 @@ ApplicationWindow {
         id: initFailure
         visible: false
     }
+
+    function locationStr() {
+        return ctx.currentLocation + ", " + ctx.currentCountry
+    }
+
+    property alias brReconnect:bridgesTab.displayReconnect
+
 }