diff --git a/src/main/kotlin/org/briarproject/briar/desktop/BriarService.kt b/src/main/kotlin/org/briarproject/briar/desktop/BriarService.kt
index aa277f516012bf86ff48aa06a690f7e91a95dabe..afb54a129da15b10530a3b8bcc37e32172b4356e 100644
--- a/src/main/kotlin/org/briarproject/briar/desktop/BriarService.kt
+++ b/src/main/kotlin/org/briarproject/briar/desktop/BriarService.kt
@@ -1,6 +1,16 @@
 package org.briarproject.briar.desktop
 
+import androidx.compose.desktop.Window
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
 import org.briarproject.bramble.api.account.AccountManager
+import org.briarproject.bramble.api.crypto.DecryptionException
 import org.briarproject.bramble.api.crypto.PasswordStrengthEstimator
 import org.briarproject.bramble.api.lifecycle.LifecycleManager
 import org.briarproject.briar.desktop.dialogs.Login
@@ -8,8 +18,14 @@ import org.briarproject.briar.desktop.dialogs.Registration
 import javax.annotation.concurrent.Immutable
 import javax.inject.Inject
 import javax.inject.Singleton
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.*
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
 
 interface BriarService {
+    @Composable
     fun start()
     fun stop()
 }
@@ -24,6 +40,7 @@ constructor(
     private val passwordStrengthEstimator: PasswordStrengthEstimator
 ) : BriarService {
 
+    @Composable
     override fun start() {
         if (!accountManager.accountExists()) {
             createAccount()
@@ -42,7 +59,39 @@ constructor(
         Registration("Briar", accountManager, lifecycleManager)
     }
 
+    @Composable
     private fun login() {
-        Login("Briar", accountManager, lifecycleManager)
+        val title = "Briar Desktop"
+        var screenState by remember { mutableStateOf<Screen>(Screen.Login) }
+        Window(title = title) {
+            when (val screen = screenState) {
+                is Screen.Login ->
+                    Login("Briar", onResult = {
+                        try {
+                            accountManager.signIn(it)
+                            signedIn()
+                            screenState = Screen.Main
+                        } catch (e: DecryptionException) {
+                            // failure, try again
+                        }
+                    })
+
+                is Screen.Main ->
+                    Column(
+                        modifier = Modifier.padding(16.dp).fillMaxSize(),
+                        verticalArrangement = Arrangement.Center,
+                        horizontalAlignment = Alignment.CenterHorizontally
+                    ) {
+                        Text("Welcome to Briar")
+                    }
+            }
+        }
     }
+
+    private fun signedIn() {
+        val dbKey = accountManager.databaseKey ?: throw AssertionError()
+        lifecycleManager.startServices(dbKey)
+        lifecycleManager.waitForStartup()
+    }
+
 }
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/Main.kt b/src/main/kotlin/org/briarproject/briar/desktop/Main.kt
index a7006b69f91abe67c0470fb6e070d1628bd57f16..50c7cec8a88a86e1c964b00398ca47883aa52ba7 100644
--- a/src/main/kotlin/org/briarproject/briar/desktop/Main.kt
+++ b/src/main/kotlin/org/briarproject/briar/desktop/Main.kt
@@ -1,5 +1,8 @@
 package org.briarproject.briar.desktop
 
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.ExperimentalComposeUiApi
+import androidx.compose.ui.window.application
 import com.github.ajalt.clikt.core.CliktCommand
 import com.github.ajalt.clikt.parameters.options.counted
 import com.github.ajalt.clikt.parameters.options.default
@@ -38,7 +41,8 @@ private class Main : CliktCommand(
         envvar = "BRIAR_DATA_DIR"
     ).default(DEFAULT_DATA_DIR)
 
-    override fun run() {
+    @OptIn(ExperimentalComposeUiApi::class)
+    override fun run() = application {
         val level = if (debug) ALL else when (verbosity) {
             0 -> WARNING
             1 -> INFO
@@ -58,7 +62,6 @@ private class Main : CliktCommand(
         BriarCoreEagerSingletons.Helper.injectEagerSingletons(app)
 
         app.getUI().startBriar()
-        app.getUI().startUI()
     }
 
     private fun getDataDir(): File {
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/MainUI.kt b/src/main/kotlin/org/briarproject/briar/desktop/MainUI.kt
deleted file mode 100644
index 1c4e79540233f55da682bbf752d06511a088ca2a..0000000000000000000000000000000000000000
--- a/src/main/kotlin/org/briarproject/briar/desktop/MainUI.kt
+++ /dev/null
@@ -1,37 +0,0 @@
-package org.briarproject.briar.desktop
-
-import org.briarproject.bramble.api.account.AccountManager
-import org.briarproject.bramble.api.contact.ContactManager
-import org.briarproject.bramble.api.crypto.PasswordStrengthEstimator
-import org.briarproject.bramble.api.event.EventBus
-import org.briarproject.briar.api.conversation.ConversationManager
-import org.briarproject.briar.api.introduction.IntroductionManager
-import org.briarproject.briar.api.messaging.MessagingManager
-import org.briarproject.briar.api.messaging.PrivateMessageFactory
-
-class MainUI(
-    private val briarService: BriarService,
-    val accountManager: AccountManager,
-    val contactManager: ContactManager,
-    private val conversationManager: ConversationManager,
-    private val messagingManager: MessagingManager,
-    private val introductionManager: IntroductionManager,
-    private val privateMessageFactory: PrivateMessageFactory,
-    private val eventBus: EventBus,
-    val passwordStrengthEstimator: PasswordStrengthEstimator
-) {
-
-    init {
-        // Should be shown only when logged in
-//        val title = "Briar Desktop"
-//        Window (title = title) {
-//            Column(
-//                modifier = Modifier.padding(16.dp).fillMaxSize(),
-//                verticalArrangement = Arrangement.Center,
-//                horizontalAlignment = Alignment.CenterHorizontally
-//            ) {
-//                Text("Welcome to Briar")
-//            }
-//        }
-    }
-}
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/Screen.kt b/src/main/kotlin/org/briarproject/briar/desktop/Screen.kt
new file mode 100644
index 0000000000000000000000000000000000000000..61e48461ac303609e9532d52677151e28a8fa3f0
--- /dev/null
+++ b/src/main/kotlin/org/briarproject/briar/desktop/Screen.kt
@@ -0,0 +1,6 @@
+package org.briarproject.briar.desktop
+
+sealed class Screen {
+    object Login: Screen()
+    object Main: Screen()
+}
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/UI.kt b/src/main/kotlin/org/briarproject/briar/desktop/UI.kt
index 957e2d004a6100a36fb83800b95f5d6705ff9e04..71d4dc59cace507092df430208fa5482accb95e5 100644
--- a/src/main/kotlin/org/briarproject/briar/desktop/UI.kt
+++ b/src/main/kotlin/org/briarproject/briar/desktop/UI.kt
@@ -1,5 +1,6 @@
 package org.briarproject.briar.desktop
 
+import androidx.compose.runtime.Composable
 import org.briarproject.bramble.api.account.AccountManager
 import org.briarproject.bramble.api.contact.ContactManager
 import org.briarproject.bramble.api.crypto.PasswordStrengthEstimator
@@ -31,24 +32,11 @@ constructor(
 
     private val logger = getLogger(UI::javaClass.name)
 
+    @Composable
     internal fun startBriar() {
         briarService.start();
     }
 
-    internal fun startUI() {
-        MainUI(
-            briarService,
-            accountManager,
-            contactManager,
-            conversationManager,
-            messagingManager,
-            introductionManager,
-            privateMessageFactory,
-            eventBus,
-            passwordStrengthEstimator
-        )
-    }
-
     internal fun getContactManager(): ContactManager {
         return contactManager
     }
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/dialogs/Login.kt b/src/main/kotlin/org/briarproject/briar/desktop/dialogs/Login.kt
index 24c8546caa630a47f9bd27b6d2ad99b49ec6373c..49942296d8913dc63929df8c9d2a7a44a82e3fe4 100644
--- a/src/main/kotlin/org/briarproject/briar/desktop/dialogs/Login.kt
+++ b/src/main/kotlin/org/briarproject/briar/desktop/dialogs/Login.kt
@@ -13,21 +13,21 @@ import androidx.compose.ui.Modifier
 import androidx.compose.ui.draw.clip
 import androidx.compose.ui.res.svgResource
 import androidx.compose.ui.unit.dp
-import org.briarproject.bramble.api.account.AccountManager
-import org.briarproject.bramble.api.lifecycle.LifecycleManager
 
 // TODO: Error handling
-fun Login(title: String, accountManager: AccountManager, lifecycleManager: LifecycleManager) =
-    Window(title = title) {
-        Column(
-            modifier = Modifier.padding(16.dp).fillMaxSize(),
-            verticalArrangement = Arrangement.Center,
-            horizontalAlignment = Alignment.CenterHorizontally
-        ) {
-            TheImage()
-            Spacer(Modifier.height(32.dp))
-            TheTextField(accountManager, lifecycleManager)
-        }
+@Composable
+fun Login(
+    title: String,
+    onResult: (result: String) -> Unit
+) =
+    Column(
+        modifier = Modifier.padding(16.dp).fillMaxSize(),
+        verticalArrangement = Arrangement.Center,
+        horizontalAlignment = Alignment.CenterHorizontally
+    ) {
+        TheImage()
+        Spacer(Modifier.height(32.dp))
+        TheTextField(onResult)
     }
 
 @Composable
@@ -42,16 +42,12 @@ private fun TheImage() {
 }
 
 @Composable
-private fun TheTextField(accountManager: AccountManager, lifecycleManager: LifecycleManager) {
+private fun TheTextField(onResult: (result: String) -> Unit) {
     var password by remember { mutableStateOf("") }
     OutlinedTextField(password, { password = it }, label = { Text("Password") })
     Spacer(Modifier.height(16.dp))
     Button(onClick = {
-        accountManager.signIn(password)
-
-        val dbKey = accountManager.databaseKey ?: throw AssertionError()
-        lifecycleManager.startServices(dbKey)
-        lifecycleManager.waitForStartup()
+        onResult.invoke(password)
     }) {
         Text("Login")
     }