diff --git a/package-lock.json b/package-lock.json
index 65fbb900d..00f6cb1b8 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -14,7 +14,7 @@
         "@chakra-ui/react": "^2.8.2",
         "@emotion/react": "^11.11.4",
         "@emotion/styled": "^11.11.5",
-        "@microbit/microbit-connection": "^0.0.0-alpha.13",
+        "@microbit/microbit-connection": "^0.0.0-alpha.14",
         "@tensorflow/tfjs": "^4.4.0",
         "@types/w3c-web-serial": "^1.0.6",
         "@types/w3c-web-usb": "^1.0.6",
@@ -4356,9 +4356,9 @@
       "integrity": "sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw=="
     },
     "node_modules/@microbit/microbit-connection": {
-      "version": "0.0.0-alpha.13",
-      "resolved": "https://registry.npmjs.org/@microbit/microbit-connection/-/microbit-connection-0.0.0-alpha.13.tgz",
-      "integrity": "sha512-TwlKERm+WmcA4wFK5ioV5S/ah71SEH4wFqNdrfgyMO5U1QZfmG+H3DEMWBuYiI67NLn4ZED71vadgSMzwuOaxg==",
+      "version": "0.0.0-alpha.14",
+      "resolved": "https://registry.npmjs.org/@microbit/microbit-connection/-/microbit-connection-0.0.0-alpha.14.tgz",
+      "integrity": "sha512-Z6PsxYTO359KD5hah0JOlxxehhlzmd7Q45NcrDYT6njTjbAT9FbSF4KnnQbgnbRgePmBzW4q/HAUl89Nvqevdw==",
       "dependencies": {
         "@microbit/microbit-universal-hex": "^0.2.2",
         "@types/web-bluetooth": "^0.0.20",
diff --git a/package.json b/package.json
index 7d0e5ab91..c3107931d 100644
--- a/package.json
+++ b/package.json
@@ -61,7 +61,7 @@
     "@chakra-ui/react": "^2.8.2",
     "@emotion/react": "^11.11.4",
     "@emotion/styled": "^11.11.5",
-    "@microbit/microbit-connection": "^0.0.0-alpha.13",
+    "@microbit/microbit-connection": "^0.0.0-alpha.14",
     "@tensorflow/tfjs": "^4.4.0",
     "@types/w3c-web-serial": "^1.0.6",
     "@types/w3c-web-usb": "^1.0.6",
diff --git a/src/App.tsx b/src/App.tsx
index 2efcfe26a..0570011b6 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -25,6 +25,7 @@ import { GesturesProvider } from "./gestures-hooks";
 import { MlStatusProvider } from "./ml-status-hooks";
 import { ConnectionStageProvider } from "./connection-stage-hooks";
 import { ConnectProvider } from "./connect-actions-hooks";
+import { ConnectStatusProvider } from "./connect-status-hooks";
 
 export interface ProviderLayoutProps {
   children: ReactNode;
@@ -43,13 +44,15 @@ const Providers = ({ children }: ProviderLayoutProps) => {
             
               
                 
-                  
-                    
-                      
-                        {children}
-                      
-                    
-                  
+                  
+                    
+                      
+                        
+                          {children}
+                        
+                      
+                    
+                  
                 
               
             
diff --git a/src/components/ConnectCableDialog.tsx b/src/components/ConnectCableDialog.tsx
index 5e17e1d84..e641f5846 100644
--- a/src/components/ConnectCableDialog.tsx
+++ b/src/components/ConnectCableDialog.tsx
@@ -1,4 +1,4 @@
-import { Image, Text, VStack } from "@chakra-ui/react";
+import { Button, Image, Text, VStack } from "@chakra-ui/react";
 import { FormattedMessage } from "react-intl";
 import connectCableImage from "../images/connect-cable.gif";
 import ConnectContainerDialog, {
@@ -51,7 +51,7 @@ const ConnectCableDialog = ({
   onSwitch,
   ...props
 }: ConnectCableDialogProps) => {
-  const { subtitleId, onLink, ...typeProps } = configs[type];
+  const { subtitleId, onLink, linkTextId, headingId } = configs[type];
   const linkConfig = {
     [LinkType.None]: undefined,
     [LinkType.Skip]: onSkip,
@@ -59,9 +59,15 @@ const ConnectCableDialog = ({
   };
   return (
     
+            
+          
+        )
+      }
     >
       
         
diff --git a/src/components/ConnectContainerDialog.tsx b/src/components/ConnectContainerDialog.tsx
index f0add80ce..9b4148564 100644
--- a/src/components/ConnectContainerDialog.tsx
+++ b/src/components/ConnectContainerDialog.tsx
@@ -19,8 +19,7 @@ export interface ConnectContainerDialogProps {
   isOpen: boolean;
   onClose: () => void;
   headingId: string;
-  onLinkClick?: () => void;
-  linkTextId?: string;
+  footerLeft?: ReactNode;
   onNextClick?: () => void;
   children: ReactNode;
   onBackClick?: () => void;
@@ -30,8 +29,7 @@ const ConnectContainerDialog = ({
   isOpen,
   onClose,
   headingId,
-  onLinkClick,
-  linkTextId,
+  footerLeft,
   onNextClick,
   onBackClick,
   children,
@@ -57,14 +55,10 @@ const ConnectContainerDialog = ({
             
           
           
-            {onLinkClick && linkTextId && (
-              
-            )}
+            {footerLeft && footerLeft}
             
               {onBackClick && (
                 
             )}
-            {stage.status === ConnectionStatus.Reconnecting && (
+            {status === ConnectionStatus.Reconnecting && (
               
                 
               
diff --git a/src/components/ReconnectErrorDialog.tsx b/src/components/ReconnectErrorDialog.tsx
index 76567c50b..717aa4b83 100644
--- a/src/components/ReconnectErrorDialog.tsx
+++ b/src/components/ReconnectErrorDialog.tsx
@@ -26,8 +26,8 @@ interface ReconnectErrorDialogProps {
   onReconnect: () => void;
   flowType: ConnectionFlowType;
   errorStep:
-    | ConnectionFlowStep.ReconnectManualFail
-    | ConnectionFlowStep.ReconnectAutoFail;
+    | ConnectionFlowStep.ReconnectFailed
+    | ConnectionFlowStep.ConnectionLost;
 }
 
 const contentConfig = {
@@ -55,8 +55,8 @@ const contentConfig = {
 };
 
 const errorTextIdPrefixConfig = {
-  [ConnectionFlowStep.ReconnectAutoFail]: "disconnectedWarning",
-  [ConnectionFlowStep.ReconnectManualFail]: "reconnectFailed",
+  [ConnectionFlowStep.ConnectionLost]: "disconnectedWarning",
+  [ConnectionFlowStep.ReconnectFailed]: "reconnectFailed",
 };
 
 const ReconnectErrorDialog = ({
diff --git a/src/components/WhatYouWillNeedDialog.tsx b/src/components/WhatYouWillNeedDialog.tsx
index 0098db768..efc6455fe 100644
--- a/src/components/WhatYouWillNeedDialog.tsx
+++ b/src/components/WhatYouWillNeedDialog.tsx
@@ -1,4 +1,4 @@
-import { Grid, GridItem, Image, Text, VStack } from "@chakra-ui/react";
+import { Button, Grid, GridItem, Image, Text, VStack } from "@chakra-ui/react";
 import { FormattedMessage } from "react-intl";
 import batteryPackImage from "../images/stylised-battery-pack.svg";
 import microbitImage from "../images/stylised-microbit-black.svg";
@@ -9,6 +9,7 @@ import computerBluetoothImage from "../images/stylised_computer_w_bluetooth.svg"
 import ConnectContainerDialog, {
   ConnectContainerDialogProps,
 } from "./ConnectContainerDialog";
+import ExternalLink from "./ExternalLink";
 
 const itemsConfig = {
   radio: [
@@ -60,24 +61,42 @@ export interface WhatYouWillNeedDialogProps
   > {
   reconnect: boolean;
   type: "radio" | "bluetooth";
+  onLinkClick: (() => void) | undefined;
 }
 
 const WhatYouWillNeedDialog = ({
   reconnect,
   type,
+  onLinkClick,
   ...props
 }: WhatYouWillNeedDialogProps) => {
   return (
     
+          {onLinkClick && (
+            
+              
+            
+          )}
+          {reconnect && (
+            
+          )}
+        
+      }
     >
       {reconnect && (
         
diff --git a/src/connect-actions.ts b/src/connect-actions.ts
index 9db6a9de0..6516ed0b0 100644
--- a/src/connect-actions.ts
+++ b/src/connect-actions.ts
@@ -1,5 +1,6 @@
 import {
   AccelerometerDataEvent,
+  ConnectionStatusEvent,
   ButtonEvent,
   ConnectionStatus as DeviceConnectionStatus,
   DeviceError,
@@ -78,7 +79,7 @@ export class ConnectActions {
     try {
       await this.usb.flash(data, {
         partial: true,
-        progress: (v) => progress(v ?? 100),
+        progress: (v: number | undefined) => progress(v ?? 100),
       });
       return ConnectAndFlashResult.Success;
     } catch (e) {
@@ -118,16 +119,16 @@ export class ConnectActions {
   };
 
   connectBluetooth = async (
-    name: string | undefined
-  ): Promise => {
+    name: string | undefined,
+    clearDevice: boolean
+  ): Promise => {
+    if (clearDevice) {
+      await this.bluetooth.clearDevice();
+    }
     if (name) {
       this.bluetooth.setNameFilter(name);
     }
     await this.bluetooth.connect();
-    if (this.bluetooth.status === DeviceConnectionStatus.CONNECTED) {
-      return ConnectResult.Success;
-    }
-    return ConnectResult.ManualConnectFailed;
   };
 
   addAccelerometerListener = (
@@ -166,4 +167,12 @@ export class ConnectActions {
     await this.bluetooth.disconnect();
     await this.radioBridge.disconnect();
   };
+
+  addStatusListener = (listener: (e: ConnectionStatusEvent) => void) => {
+    this.bluetooth.addEventListener("status", listener);
+  };
+
+  removeStatusListener = (listener: (e: ConnectionStatusEvent) => void) => {
+    this.bluetooth.removeEventListener("status", listener);
+  };
 }
diff --git a/src/connect-status-hooks.tsx b/src/connect-status-hooks.tsx
new file mode 100644
index 000000000..4e7c7864c
--- /dev/null
+++ b/src/connect-status-hooks.tsx
@@ -0,0 +1,189 @@
+import {
+  ConnectionStatusEvent,
+  ConnectionStatus as DeviceConnectionStatus,
+} from "@microbit/microbit-connection";
+import {
+  MutableRefObject,
+  ReactNode,
+  createContext,
+  useContext,
+  useEffect,
+  useRef,
+  useState,
+} from "react";
+import { useConnectActions } from "./connect-actions-hooks";
+
+export enum ConnectionStatus {
+  /**
+   * Represents the initial connection status.
+   */
+  NotConnected = "NotConnected",
+  /**
+   * Connecting occurs for the initial connection.
+   */
+  Connecting = "Connecting",
+  /**
+   * Connected.
+   */
+  Connected = "Connected",
+  /**
+   * Reconnecting occurs for the subsequent connections after the initial one.
+   */
+  Reconnecting = "Reconnecting",
+  /**
+   * Disconnected. The disconnection is triggered by the user.
+   */
+  Disconnected = "Disconnected",
+  /**
+   * Failure to establish initial connection triggered by the user.
+   */
+  FailedToConnect = "FailedToConnect",
+  /**
+   * Failure to reconnect triggered by the user.
+   */
+  FailedToReconnect = "FailedToReconnect",
+  /**
+   *  Connection lost. Auto-reconnect was attempted, but failed.
+   */
+  ConnectionLost = "ConnectionLost",
+  /**
+   * A subsequent failure to reconnect after a reconnection failure.
+   * The initial reconnection failure may have been triggered automatically
+   * or by the user (ConnectionLost or FailedToReconnect).
+   */
+  FailedToReconnectTwice = "FailedToReconnectTwice",
+}
+
+type ConnectStatusContextValue = [
+  ConnectionStatus,
+  (status: ConnectionStatus) => void
+];
+
+const ConnectStatusContext = createContext(
+  null
+);
+
+interface ConnectStatusProviderProps {
+  children: ReactNode;
+}
+
+export const ConnectStatusProvider = ({
+  children,
+}: ConnectStatusProviderProps) => {
+  const connectStatusContextValue = useState(
+    ConnectionStatus.NotConnected
+  );
+  return (
+    
+      {children}
+    
+  );
+};
+
+export const useSetConnectStatus = (): ((status: ConnectionStatus) => void) => {
+  const connectStatusContextValue = useContext(ConnectStatusContext);
+  if (!connectStatusContextValue) {
+    throw new Error("Missing provider");
+  }
+  const [, setStatus] = connectStatusContextValue;
+
+  return setStatus;
+};
+
+export const useConnectStatus = (
+  handleStatus?: (status: ConnectionStatus) => void
+): ConnectionStatus => {
+  const connectStatusContextValue = useContext(ConnectStatusContext);
+  if (!connectStatusContextValue) {
+    throw new Error("Missing provider");
+  }
+  const [status, setStatus] = connectStatusContextValue;
+  const connectActions = useConnectActions();
+  const prevDeviceStatus = useRef(null);
+  const hasAttempedReconnect = useRef(false);
+
+  useEffect(() => {
+    const listener = ({ status: deviceStatus }: ConnectionStatusEvent) => {
+      const newStatus = getNextConnectionStatus(
+        status,
+        deviceStatus,
+        prevDeviceStatus.current,
+        hasAttempedReconnect
+      );
+      prevDeviceStatus.current = deviceStatus;
+      if (newStatus) {
+        handleStatus && handleStatus(newStatus);
+        setStatus(newStatus);
+      }
+    };
+    connectActions.addStatusListener(listener);
+    return () => {
+      connectActions.removeStatusListener(listener);
+    };
+  }, [connectActions, handleStatus, setStatus, status]);
+
+  return status;
+};
+
+const getNextConnectionStatus = (
+  status: ConnectionStatus,
+  deviceStatus: DeviceConnectionStatus,
+  prevDeviceStatus: DeviceConnectionStatus | null,
+  hasAttempedReconnect: MutableRefObject
+) => {
+  if (
+    // Disconnection happens for newly started / restarted
+    // connection flows when clearing device
+    deviceStatus === DeviceConnectionStatus.DISCONNECTED &&
+    status === ConnectionStatus.NotConnected
+  ) {
+    return ConnectionStatus.NotConnected;
+  }
+  if (deviceStatus === DeviceConnectionStatus.CONNECTED) {
+    hasAttempedReconnect.current = false;
+    return ConnectionStatus.Connected;
+  }
+  if (
+    (status === ConnectionStatus.Connecting &&
+      deviceStatus === DeviceConnectionStatus.DISCONNECTED) ||
+    // If user does not select a device
+    (deviceStatus === DeviceConnectionStatus.NO_AUTHORIZED_DEVICE &&
+      prevDeviceStatus === DeviceConnectionStatus.NO_AUTHORIZED_DEVICE)
+  ) {
+    return ConnectionStatus.FailedToConnect;
+  }
+  if (
+    hasAttempedReconnect.current &&
+    deviceStatus === DeviceConnectionStatus.DISCONNECTED &&
+    prevDeviceStatus === DeviceConnectionStatus.CONNECTING
+  ) {
+    return ConnectionStatus.FailedToReconnectTwice;
+  }
+  if (
+    deviceStatus === DeviceConnectionStatus.DISCONNECTED &&
+    prevDeviceStatus === DeviceConnectionStatus.CONNECTING
+  ) {
+    hasAttempedReconnect.current = true;
+    return ConnectionStatus.FailedToReconnect;
+  }
+  if (
+    deviceStatus === DeviceConnectionStatus.DISCONNECTED &&
+    prevDeviceStatus === DeviceConnectionStatus.RECONNECTING
+  ) {
+    hasAttempedReconnect.current = true;
+    return ConnectionStatus.ConnectionLost;
+  }
+  if (deviceStatus === DeviceConnectionStatus.DISCONNECTED) {
+    return ConnectionStatus.Disconnected;
+  }
+  if (
+    deviceStatus === DeviceConnectionStatus.RECONNECTING ||
+    deviceStatus === DeviceConnectionStatus.CONNECTING
+  ) {
+    return status === ConnectionStatus.NotConnected ||
+      status === ConnectionStatus.FailedToConnect
+      ? ConnectionStatus.Connecting
+      : ConnectionStatus.Reconnecting;
+  }
+  return undefined;
+};
diff --git a/src/connection-stage-actions.ts b/src/connection-stage-actions.ts
index 17beb78ba..e7a59fc7c 100644
--- a/src/connection-stage-actions.ts
+++ b/src/connection-stage-actions.ts
@@ -10,9 +10,10 @@ import {
   ConnectionFlowStep,
   ConnectionFlowType,
   ConnectionStage,
-  ConnectionStatus,
+  ConnectionType,
 } from "./connection-stage-hooks";
 import { createStepPageUrl } from "./urls";
+import { ConnectionStatus } from "./connect-status-hooks";
 
 type FlowStage = Pick;
 
@@ -21,12 +22,15 @@ export class ConnectionStageActions {
     private actions: ConnectActions,
     private navigate: NavigateFunction,
     private stage: ConnectionStage,
-    private setStage: (stage: ConnectionStage) => void
+    private setStage: (stage: ConnectionStage) => void,
+    private setStatus: (status: ConnectionStatus) => void
   ) {}
 
-  start = () =>
+  start = () => {
+    this.setStatus(ConnectionStatus.NotConnected);
     this.setStage({
       ...this.stage,
+      hasFailedToReconnectTwice: false,
       flowType:
         this.stage.flowType === ConnectionFlowType.RadioBridge
           ? ConnectionFlowType.RadioRemote
@@ -36,6 +40,7 @@ export class ConnectionStageActions {
           ? ConnectionFlowStep.WebUsbBluetoothUnsupported
           : ConnectionFlowStep.Start,
     });
+  };
 
   setFlowStep = (step: ConnectionFlowStep) => {
     this.setStage({ ...this.stage, flowStep: step });
@@ -79,11 +84,8 @@ export class ConnectionStageActions {
       }
       case ConnectionFlowType.RadioBridge: {
         newStage = {
-          ...this.stage,
-          connType: "radio",
-          flowStep: ConnectionFlowStep.ConnectingMicrobits,
+          ...this.getConnectingStage("radio"),
           radioBridgeDeviceId: deviceId,
-          status: this.getConnectingOrReconnectingStatus(),
         };
         break;
       }
@@ -136,17 +138,12 @@ export class ConnectionStageActions {
     });
   };
 
-  connectBluetooth = async () => {
-    this.setStage({
-      ...this.stage,
-      connType: "bluetooth",
-      flowStep: ConnectionFlowStep.ConnectingBluetooth,
-      status: this.getConnectingOrReconnectingStatus(),
-    });
-    const result = await this.actions.connectBluetooth(
-      this.stage.bluetoothMicrobitName
+  connectBluetooth = async (clearDevice: boolean = true) => {
+    this.setStage(this.getConnectingStage("bluetooth"));
+    await this.actions.connectBluetooth(
+      this.stage.bluetoothMicrobitName,
+      clearDevice
     );
-    this.handleConnectResult(result);
   };
 
   connectMicrobits = async () => {
@@ -160,76 +157,79 @@ export class ConnectionStageActions {
     }
   };
 
-  private getConnectingOrReconnectingStatus = () => {
-    return this.stage.status === ConnectionStatus.None
-      ? ConnectionStatus.Connecting
-      : ConnectionStatus.Reconnecting;
+  private getConnectingStage = (connType: ConnectionType) => {
+    return {
+      ...this.stage,
+      connType,
+      flowStep:
+        connType === "bluetooth"
+          ? ConnectionFlowStep.ConnectingBluetooth
+          : ConnectionFlowStep.ConnectingMicrobits,
+    };
   };
 
   private handleConnectResult = (result: ConnectResult) => {
     if (result === ConnectResult.Success) {
+      // TODO: Remove forced set status and listen to status event
+      // from connection library instead for radio
+      if (this.stage.connType === "radio") {
+        this.setStatus(ConnectionStatus.Connected);
+      }
       return this.onConnected();
     }
-    const newReconnectFailStreak =
-      this.stage.status === ConnectionStatus.Reconnecting
-        ? this.stage.reconnectFailStreak + 1
-        : this.stage.reconnectFailStreak;
-
-    const nextFlowStep = this.getReconnectFailFlowStep(
-      newReconnectFailStreak,
-      result
-    );
-    this.setStage({
-      ...this.stage,
-      reconnectFailStreak: newReconnectFailStreak,
-      status: ConnectionStatus.Disconnected,
-      flowStep: nextFlowStep,
-    });
+    this.handleConnectFail();
   };
 
-  private getReconnectFailFlowStep = (
-    failStreak: number,
-    result: ConnectResult
-  ) => {
-    switch (failStreak) {
-      case 0: {
-        return this.stage.flowType === ConnectionFlowType.Bluetooth
-          ? ConnectionFlowStep.TryAgainBluetoothConnect
-          : ConnectionFlowStep.TryAgainReplugMicrobit;
-      }
-      case 1: {
-        return result === ConnectResult.ManualConnectFailed
-          ? ConnectionFlowStep.ReconnectManualFail
-          : ConnectionFlowStep.ReconnectAutoFail;
-      }
-      default: {
-        return ConnectionFlowStep.ReconnectFailedTwice;
-      }
-    }
+  private handleConnectFail = () => {
+    this.setFlowStep(
+      this.stage.flowType === ConnectionFlowType.Bluetooth
+        ? ConnectionFlowStep.TryAgainBluetoothConnect
+        : ConnectionFlowStep.TryAgainReplugMicrobit
+    );
   };
 
   private onConnected = () => {
-    this.setStage({
-      ...this.stage,
-      flowStep: ConnectionFlowStep.None,
-      status: ConnectionStatus.Connected,
-      reconnectFailStreak: 0,
-    });
+    this.setFlowStep(ConnectionFlowStep.None);
     this.navigate(createStepPageUrl("add-data"));
   };
 
   disconnect = async () => {
     await this.actions.disconnect();
-    this.setStage({
-      ...this.stage,
-      status: ConnectionStatus.Disconnected,
-    });
+  };
+
+  handleConnectionStatus = (status: ConnectionStatus) => {
+    switch (status) {
+      case ConnectionStatus.Connected: {
+        return this.onConnected();
+      }
+      case ConnectionStatus.FailedToConnect: {
+        return this.handleConnectFail();
+      }
+      case ConnectionStatus.FailedToReconnectTwice: {
+        return this.setStage({
+          ...this.stage,
+          hasFailedToReconnectTwice: true,
+          flowStep: ConnectionFlowStep.ReconnectFailedTwice,
+        });
+      }
+      case ConnectionStatus.FailedToReconnect: {
+        return this.setFlowStep(ConnectionFlowStep.ReconnectFailed);
+      }
+      case ConnectionStatus.ConnectionLost: {
+        return this.setFlowStep(ConnectionFlowStep.ConnectionLost);
+      }
+      case ConnectionStatus.Reconnecting: {
+        return this.setStage(this.getConnectingStage("bluetooth"));
+      }
+    }
+    return;
   };
 
   reconnect = async () => {
     if (this.stage.connType === "bluetooth") {
-      await this.connectBluetooth();
+      await this.connectBluetooth(false);
     } else {
+      this.setStage(this.getConnectingStage("radio"));
       await this.connectMicrobits();
     }
   };
@@ -252,12 +252,30 @@ export class ConnectionStageActions {
     });
   };
 
+  private getStagesOrder = () => {
+    if (this.stage.flowType === ConnectionFlowType.Bluetooth) {
+      return bluetoothFlow({
+        isManualFlashing:
+          !this.stage.isWebUsbSupported ||
+          this.stage.flowStep === ConnectionFlowStep.ManualFlashingTutorial,
+        isRestartAgain: this.stage.hasFailedToReconnectTwice,
+      });
+    }
+    return radioFlow();
+  };
+
   onNextClick = () => {
-    this.setStage({ ...this.stage, ...getNextStage(this.stage, 1) });
+    this.setStage({
+      ...this.stage,
+      ...getNextStage(this.stage, 1, this.getStagesOrder()),
+    });
   };
 
   onBackClick = () => {
-    this.setStage({ ...this.stage, ...getNextStage(this.stage, -1) });
+    this.setStage({
+      ...this.stage,
+      ...getNextStage(this.stage, -1, this.getStagesOrder()),
+    });
   };
 
   onTryAgain = () => {
@@ -269,47 +287,70 @@ export class ConnectionStageActions {
   };
 }
 
-const getStagesOrder = (state: ConnectionStage): FlowStage[] => {
-  const { RadioRemote, RadioBridge, Bluetooth } = ConnectionFlowType;
-  if (state.flowType === ConnectionFlowType.Bluetooth) {
-    return [
-      { flowStep: ConnectionFlowStep.Start, flowType: Bluetooth },
-      { flowStep: ConnectionFlowStep.ConnectCable, flowType: Bluetooth },
-      // Only bluetooth mode has this fallback, the radio bridge mode requires working WebUSB.
-      {
-        flowStep:
-          !state.isWebUsbSupported ||
-          state.flowStep === ConnectionFlowStep.ManualFlashingTutorial
-            ? ConnectionFlowStep.ManualFlashingTutorial
-            : ConnectionFlowStep.WebUsbFlashingTutorial,
-        flowType: Bluetooth,
-      },
-      { flowStep: ConnectionFlowStep.ConnectBattery, flowType: Bluetooth },
-      {
-        flowStep: ConnectionFlowStep.EnterBluetoothPattern,
-        flowType: Bluetooth,
-      },
-      {
-        flowStep: ConnectionFlowStep.ConnectBluetoothTutorial,
-        flowType: Bluetooth,
-      },
-    ];
-  }
-  return [
-    { flowStep: ConnectionFlowStep.Start, flowType: RadioRemote },
-    { flowStep: ConnectionFlowStep.ConnectCable, flowType: RadioRemote },
-    {
-      flowStep: ConnectionFlowStep.WebUsbFlashingTutorial,
-      flowType: RadioRemote,
-    },
-    { flowStep: ConnectionFlowStep.ConnectBattery, flowType: RadioRemote },
-    { flowStep: ConnectionFlowStep.ConnectCable, flowType: RadioBridge },
-    {
-      flowStep: ConnectionFlowStep.WebUsbFlashingTutorial,
-      flowType: RadioBridge,
-    },
-  ];
-};
+const bluetoothFlow = ({
+  isManualFlashing,
+  isRestartAgain,
+}: {
+  isManualFlashing: boolean;
+  isRestartAgain: boolean;
+}) => [
+  {
+    flowStep: isRestartAgain
+      ? ConnectionFlowStep.ReconnectFailedTwice
+      : ConnectionFlowStep.Start,
+    flowType: ConnectionFlowType.Bluetooth,
+  },
+  {
+    flowStep: ConnectionFlowStep.ConnectCable,
+    flowType: ConnectionFlowType.Bluetooth,
+  },
+  // Only bluetooth mode has this fallback, the radio bridge mode requires working WebUSB.
+  {
+    flowStep: isManualFlashing
+      ? ConnectionFlowStep.ManualFlashingTutorial
+      : ConnectionFlowStep.WebUsbFlashingTutorial,
+    flowType: ConnectionFlowType.Bluetooth,
+  },
+  {
+    flowStep: ConnectionFlowStep.ConnectBattery,
+    flowType: ConnectionFlowType.Bluetooth,
+  },
+  {
+    flowStep: ConnectionFlowStep.EnterBluetoothPattern,
+    flowType: ConnectionFlowType.Bluetooth,
+  },
+  {
+    flowStep: ConnectionFlowStep.ConnectBluetoothTutorial,
+    flowType: ConnectionFlowType.Bluetooth,
+  },
+];
+
+const radioFlow = () => [
+  {
+    flowStep: ConnectionFlowStep.Start,
+    flowType: ConnectionFlowType.RadioRemote,
+  },
+  {
+    flowStep: ConnectionFlowStep.ConnectCable,
+    flowType: ConnectionFlowType.RadioRemote,
+  },
+  {
+    flowStep: ConnectionFlowStep.WebUsbFlashingTutorial,
+    flowType: ConnectionFlowType.RadioRemote,
+  },
+  {
+    flowStep: ConnectionFlowStep.ConnectBattery,
+    flowType: ConnectionFlowType.RadioRemote,
+  },
+  {
+    flowStep: ConnectionFlowStep.ConnectCable,
+    flowType: ConnectionFlowType.RadioBridge,
+  },
+  {
+    flowStep: ConnectionFlowStep.WebUsbFlashingTutorial,
+    flowType: ConnectionFlowType.RadioBridge,
+  },
+];
 
 const getFlowStageIdx = (
   { flowStep, flowType }: FlowStage,
@@ -324,12 +365,15 @@ const getFlowStageIdx = (
   throw new Error("Should be able to match stage and type again order");
 };
 
-const getNextStage = (stage: ConnectionStage, increment: number): FlowStage => {
-  const order = getStagesOrder(stage);
-  const currIdx = getFlowStageIdx(stage, order);
+const getNextStage = (
+  stage: ConnectionStage,
+  increment: number,
+  stagesOrder: FlowStage[]
+): FlowStage => {
+  const currIdx = getFlowStageIdx(stage, stagesOrder);
   const newIdx = currIdx + increment;
-  if (newIdx === order.length || newIdx < 0) {
+  if (newIdx === stagesOrder.length || newIdx < 0) {
     throw new Error("Impossible step stage");
   }
-  return order[newIdx];
+  return stagesOrder[newIdx];
 };
diff --git a/src/connection-stage-hooks.tsx b/src/connection-stage-hooks.tsx
index c455ca8d8..644521ce9 100644
--- a/src/connection-stage-hooks.tsx
+++ b/src/connection-stage-hooks.tsx
@@ -11,6 +11,11 @@ import { ConnectActions } from "./connect-actions";
 import { useConnectActions } from "./connect-actions-hooks";
 import { ConnectionStageActions } from "./connection-stage-actions";
 import { useStorage } from "./hooks/use-storage";
+import {
+  ConnectionStatus,
+  useConnectStatus,
+  useSetConnectStatus,
+} from "./connect-status-hooks";
 
 export enum ConnectionFlowType {
   Bluetooth = "bluetooth",
@@ -18,14 +23,6 @@ export enum ConnectionFlowType {
   RadioRemote = "remote",
 }
 
-export enum ConnectionStatus {
-  None = "None", // Have not been connected before
-  Connecting = "Connecting",
-  Connected = "Connected",
-  Disconnected = "Disconnected",
-  Reconnecting = "Reconnecting",
-}
-
 export type ConnectionType = "bluetooth" | "radio";
 
 export enum ConnectionFlowStep {
@@ -54,8 +51,8 @@ export enum ConnectionFlowStep {
   MicrobitUnsupported = "MicrobitUnsupported",
   WebUsbBluetoothUnsupported = "WebUsbBluetoothUnsupported",
 
-  ReconnectAutoFail = "ReconnectAutoFail",
-  ReconnectManualFail = "ReconnectManualFail",
+  ConnectionLost = "ConnectionLoss",
+  ReconnectFailed = "ReconnectFailed",
   ReconnectFailedTwice = "ReconnectFailedTwice",
 }
 
@@ -63,21 +60,18 @@ export interface ConnectionStage {
   // For connection flow
   flowStep: ConnectionFlowStep;
   flowType: ConnectionFlowType;
-  // Number of times there have been consecutive reconnect fails
-  // for determining which reconnection dialog to show
-  reconnectFailStreak: number;
 
   // Compatibility
   isWebBluetoothSupported: boolean;
   isWebUsbSupported: boolean;
 
   // Connection state
-  status: ConnectionStatus;
   connType: "bluetooth" | "radio";
   bluetoothDeviceId?: number;
   bluetoothMicrobitName?: string;
   radioBridgeDeviceId?: number;
   radioRemoteDeviceId?: number;
+  hasFailedToReconnectTwice: boolean;
 }
 
 type ConnectionStageContextValue = [
@@ -97,12 +91,11 @@ const getInitialConnectionStageValue = (
 ): ConnectionStage => ({
   flowStep: ConnectionFlowStep.None,
   flowType: ConnectionFlowType.Bluetooth,
-  reconnectFailStreak: 0,
-  status: ConnectionStatus.None,
   bluetoothMicrobitName: microbitName,
   connType: "bluetooth",
   isWebBluetoothSupported: true,
   isWebUsbSupported: true,
+  hasFailedToReconnectTwice: false,
 });
 
 export const ConnectionStageProvider = ({
@@ -144,20 +137,20 @@ export const useConnectionStage = (): {
   const [stage, setStage] = connectionStageContextValue;
   const navigate = useNavigate();
   const connectActions = useConnectActions();
+  const setStatus = useSetConnectStatus();
 
   const actions = useMemo(() => {
     return new ConnectionStageActions(
       connectActions,
       navigate,
       stage,
-      setStage
+      setStage,
+      setStatus
     );
-  }, [connectActions, navigate, stage, setStage]);
+  }, [connectActions, navigate, stage, setStage, setStatus]);
 
-  const isConnected = useMemo(
-    () => stage.status === ConnectionStatus.Connected,
-    [stage.status]
-  );
+  const status = useConnectStatus(actions.handleConnectionStatus);
+  const isConnected = status === ConnectionStatus.Connected;
 
   return {
     stage,