diff --git a/src/MainApplicationWindow.qml b/src/MainApplicationWindow.qml
index 17e3a9d09ebebb39c47b1f2430114ec2d48e4fe0..7808b21aab3a26a1f8039ff9a8546e52939d1468 100644
--- a/src/MainApplicationWindow.qml
+++ b/src/MainApplicationWindow.qml
@@ -52,6 +52,8 @@ ApplicationWindow {
         appContainer: appContainer
     }
 
+    property bool windowSettingsLoaded: false
+
     function checkLoadedSource() {
         var sourceString = mainApplicationLoader.source.toString()
 
@@ -80,35 +82,20 @@ ApplicationWindow {
         // is set, then we can quit
         if (force || !UtilsAdapter.getAppValue(Settings.MinimizeOnClose) ||
                 !UtilsAdapter.getAccountListSize()) {
+            // Save the window geometry and state before quitting.
+            var geometry = Qt.rect(appWindow.x, appWindow.y,
+                                   appWindow.width, appWindow.height)
+            AppSettingsManager.setValue(Settings.WindowGeometry, geometry)
+            AppSettingsManager.setValue(Settings.WindowState, appWindow.visibility)
             Qt.quit()
-        } else
+        } else {
             hide()
+        }
     }
 
     title: JamiStrings.appTitle
 
-    width: {
-        if (checkLoadedSource() === MainApplicationWindow.LoadedSource.WizardView)
-            return JamiTheme.wizardViewMinWidth
-        return JamiTheme.mainViewPreferredWidth
-    }
-    height: {
-        if (checkLoadedSource() === MainApplicationWindow.LoadedSource.WizardView)
-            return JamiTheme.wizardViewMinHeight
-        return JamiTheme.mainViewPreferredHeight
-    }
-    minimumWidth: {
-        if (checkLoadedSource() === MainApplicationWindow.LoadedSource.WizardView)
-            return JamiTheme.wizardViewMinWidth
-        return JamiTheme.mainViewMinWidth
-    }
-    minimumHeight: {
-        if (checkLoadedSource() === MainApplicationWindow.LoadedSource.WizardView)
-            return JamiTheme.wizardViewMinHeight
-        return JamiTheme.mainViewMinHeight
-    }
-
-    visible: mainApplicationLoader.status === Loader.Ready
+    visible: mainApplicationLoader.status === Loader.Ready && windowSettingsLoaded
 
     // To facilitate reparenting of the callview during
     // fullscreen mode, we need QQuickItem based object.
@@ -143,10 +130,47 @@ ApplicationWindow {
             }
         }
 
+        // Set `visible = false` when loading a new QML file.
+        onSourceChanged: windowSettingsLoaded = false
+
         onLoaded: {
             if (UtilsAdapter.getAppValue(Settings.StartMinimized)) {
                 showMinimized()
+            } else {
+                if (checkLoadedSource() === MainApplicationWindow.LoadedSource.WizardView) {
+                    appWindow.width = JamiTheme.wizardViewMinWidth
+                    appWindow.height = JamiTheme.wizardViewMinHeight
+                    appWindow.minimumWidth = JamiTheme.wizardViewMinWidth
+                    appWindow.minimumHeight = JamiTheme.wizardViewMinHeight
+                } else {
+                    // Main window, load settings if possible.
+                    var geometry = AppSettingsManager.getValue(Settings.WindowGeometry)
+
+                    // Position.
+                    if (!isNaN(geometry.x) && !isNaN(geometry.y)) {
+                        appWindow.x = geometry.x
+                        appWindow.y = geometry.y
+                    }
+
+                    // Dimensions.
+                    appWindow.width = geometry.width ?
+                                geometry.width :
+                                JamiTheme.mainViewPreferredWidth
+                    appWindow.height = geometry.height ?
+                                geometry.height :
+                                JamiTheme.mainViewPreferredHeight
+                    appWindow.minimumWidth = JamiTheme.mainViewMinWidth
+                    appWindow.minimumHeight = JamiTheme.mainViewMinHeight
+
+                    // State.
+                    const visibilityStr = AppSettingsManager.getValue(Settings.WindowState)
+                    appWindow.visibility = parseInt(visibilityStr)
+                }
             }
+
+            // This will trigger `visible = true`.
+            windowSettingsLoaded = true
+
             // Quiet check for updates on start if set to.
             if (UtilsAdapter.getAppValue(Settings.AutoUpdate)) {
                 UpdateManager.checkForUpdates(true)
@@ -170,6 +194,14 @@ ApplicationWindow {
         }
     }
 
+    Connections {
+        target: MainApplication
+
+        function onCloseRequested() {
+            close(true)
+        }
+    }
+
     Connections {
         target: {
             if (Qt.platform.os !== "windows" && Qt.platform.os !== "macos")
diff --git a/src/appsettingsmanager.cpp b/src/appsettingsmanager.cpp
index e719a19f61d076af65608f59db75765110ab191f..614a027c8b478f6d8a1e309b1d92f05a294dc23f 100644
--- a/src/appsettingsmanager.cpp
+++ b/src/appsettingsmanager.cpp
@@ -1,4 +1,4 @@
-/*!
+/*
  * Copyright (C) 2021-2022 Savoir-faire Linux Inc.
  * Author: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com>
  *
@@ -20,6 +20,9 @@
 
 #include "appsettingsmanager.h"
 
+const QString defaultDownloadPath = QStandardPaths::writableLocation(
+    QStandardPaths::DownloadLocation);
+
 AppSettingsManager::AppSettingsManager(QObject* parent)
     : QObject(parent)
     , settings_(new QSettings("jami.net", "Jami", this))
diff --git a/src/appsettingsmanager.h b/src/appsettingsmanager.h
index b18e02dd555e2399b9bf4f76d8d4799338bb8638..5c6586b1924e573bde4ac83401f48f26d45ad434 100644
--- a/src/appsettingsmanager.h
+++ b/src/appsettingsmanager.h
@@ -1,4 +1,4 @@
-/*!
+/*
  * Copyright (C) 2020-2022 Savoir-faire Linux Inc.
  * Author: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com>
  *
@@ -26,9 +26,9 @@
 #include <QObject>
 #include <QString>
 #include <QStandardPaths>
+#include <QWindow> // for QWindow::AutomaticVisibility
 
-const QString defaultDownloadPath = QStandardPaths::writableLocation(
-    QStandardPaths::DownloadLocation);
+extern const QString defaultDownloadPath;
 
 // clang-format off
 #define KEYS \
@@ -44,11 +44,13 @@ const QString defaultDownloadPath = QStandardPaths::writableLocation(
     X(EnableDarkTheme, false) \
     X(AutoUpdate, true) \
     X(StartMinimized, false) \
-    X(NeverShowMeAgain, false)
+    X(NeverShowMeAgain, false) \
+    X(WindowGeometry, QRectF(qQNaN(), qQNaN(), 0., 0.)) \
+    X(WindowState, QWindow::AutomaticVisibility)
 
 /*
  * A class to expose settings keys in both c++ and QML.
- * Note: this using a non-constructable class instead of a
+ * Note: this is using a non-constructable class instead of a
  * namespace allows for QML enum auto-completion in QtCreator.
  * This works well when there is only one enum class. Otherwise,
  * to prevent element name collision when defining multiple enums,
diff --git a/src/lrcinstance.cpp b/src/lrcinstance.cpp
index d5b52c80e2340f5f07bfa7a993bcfd5d20f162dd..2318d37cf7bee15abe8cdc31d51cb195e5c47ec7 100644
--- a/src/lrcinstance.cpp
+++ b/src/lrcinstance.cpp
@@ -25,7 +25,6 @@
 #include <QObject>
 #include <QPixmap>
 #include <QRegularExpression>
-#include <QSettings>
 #include <QtConcurrent/QtConcurrent>
 
 LRCInstance::LRCInstance(migrateCallback willMigrateCb,
diff --git a/src/mainapplication.cpp b/src/mainapplication.cpp
index 27aa5f4c93211abb83b23977c351909fbf1e5aa2..918200badbd6c674cd5b9d94477c3271a02afaa3 100644
--- a/src/mainapplication.cpp
+++ b/src/mainapplication.cpp
@@ -248,7 +248,8 @@ MainApplication::init()
 
     initQmlLayer();
 
-    settingsManager_->setValue(Settings::Key::StartMinimized, results[opts::STARTMINIMIZED].toBool());
+    settingsManager_->setValue(Settings::Key::StartMinimized,
+                               results[opts::STARTMINIMIZED].toBool());
 
     initSystray();
 
@@ -441,13 +442,14 @@ MainApplication::initSystray()
 #endif
 
     QAction* quitAction = new QAction(quitString, this);
-    connect(quitAction, &QAction::triggered, this, &MainApplication::cleanup);
+    connect(quitAction, &QAction::triggered, this, &MainApplication::closeRequested);
 
     QAction* restoreAction = new QAction(tr("&Show Jami"), this);
     connect(restoreAction, &QAction::triggered, this, &MainApplication::restoreApp);
 
     connect(systemTray_.get(),
             &QSystemTrayIcon::activated,
+            this,
             [this](QSystemTrayIcon::ActivationReason reason) {
                 if (reason != QSystemTrayIcon::ActivationReason::Context) {
 #ifdef Q_OS_WINDOWS
diff --git a/src/mainapplication.h b/src/mainapplication.h
index 2c0baf50a37c5162064a9b27a097c75855791cc8..53a1ceb0736aa4e570fc4112fa7dcc37c6463f42 100644
--- a/src/mainapplication.h
+++ b/src/mainapplication.h
@@ -65,6 +65,9 @@ public:
     bool init();
     void restoreApp();
 
+Q_SIGNALS:
+    void closeRequested();
+
 private:
     void vsConsoleDebug();
     void fileDebug(QFile* debugFile);
diff --git a/src/qmlregister.cpp b/src/qmlregister.cpp
index 9375c8d0700990f4643d5fe8254cab2daa9a0ac4..fd4dab1223076cf8ba53796e76693f1393b2248d 100644
--- a/src/qmlregister.cpp
+++ b/src/qmlregister.cpp
@@ -103,7 +103,7 @@ registerTypes(QQmlEngine* engine,
               AppSettingsManager* settingsManager,
               PreviewEngine* previewEngine,
               ScreenInfo* screenInfo,
-              QObject* parent)
+              MainApplication* parent)
 {
     // setup the adapters (their lifetimes are that of MainApplication)
     auto callAdapter = new CallAdapter(systemTray, lrcInstance, parent);
@@ -180,6 +180,7 @@ registerTypes(QQmlEngine* engine,
     QML_REGISTERSINGLETONTYPE_URL(NS_CONSTANTS, "qrc:/src/constant/JamiResources.qml", JamiResources);
     QML_REGISTERSINGLETONTYPE_URL(NS_CONSTANTS, "qrc:/src/constant/MsgSeq.qml", MsgSeq);
 
+    QML_REGISTERSINGLETONTYPE_POBJECT(NS_CONSTANTS, parent, "MainApplication")
     QML_REGISTERSINGLETONTYPE_POBJECT(NS_CONSTANTS, screenInfo, "CurrentScreenInfo")
     QML_REGISTERSINGLETONTYPE_POBJECT(NS_CONSTANTS, lrcInstance, "LRCInstance")
     QML_REGISTERSINGLETONTYPE_POBJECT(NS_CONSTANTS, settingsManager, "AppSettingsManager")
diff --git a/src/qmlregister.h b/src/qmlregister.h
index 31eded091b867c3e04c78e92c3d9bf8c818bc074..38bfd091e834c8727463a3d1dc6d3f88a5059d1a 100644
--- a/src/qmlregister.h
+++ b/src/qmlregister.h
@@ -35,6 +35,7 @@ class LRCInstance;
 class AppSettingsManager;
 class PreviewEngine;
 class ScreenInfo;
+class MainApplication;
 
 // Hack for QtCreator autocomplete (part 1)
 // https://bugreports.qt.io/browse/QTCREATORBUG-20569
@@ -66,5 +67,5 @@ void registerTypes(QQmlEngine* engine,
                    AppSettingsManager* appSettingsManager,
                    PreviewEngine* previewEngine,
                    ScreenInfo* screenInfo,
-                   QObject* parent);
+                   MainApplication* parent);
 }