diff --git a/packages/desktop-client/package.json b/packages/desktop-client/package.json
index 3e7375494adb304fbbd227f73c94d6e6a30bdae9..8eaca8ff054a4a365fee2698c4f4ea422914ad0c 100644
--- a/packages/desktop-client/package.json
+++ b/packages/desktop-client/package.json
@@ -21,6 +21,7 @@
     "@swc/plugin-react-remove-properties": "^1.5.108",
     "@testing-library/react": "14.1.2",
     "@testing-library/user-event": "14.5.2",
+    "@types/promise-retry": "^1.1.6",
     "@types/react": "^18.2.0",
     "@types/react-dom": "^18.2.1",
     "@types/react-modal": "^3.16.0",
@@ -43,6 +44,7 @@
     "mdast-util-newline-to-break": "^2.0.0",
     "memoize-one": "^6.0.0",
     "pikaday": "1.8.2",
+    "promise-retry": "^2.0.1",
     "react": "18.2.0",
     "react-aria-components": "^1.1.1",
     "react-dnd": "^16.0.1",
diff --git a/packages/desktop-client/src/components/App.tsx b/packages/desktop-client/src/components/App.tsx
index 5a63cdd58b740295ed26147bbdf4ab1ecf67103f..85073350ce9274f7f4cc204fb05dd1fa263cdb6b 100644
--- a/packages/desktop-client/src/components/App.tsx
+++ b/packages/desktop-client/src/components/App.tsx
@@ -38,24 +38,32 @@ type AppInnerProps = {
 function AppInner({ budgetId, cloudFileId }: AppInnerProps) {
   const [initializing, setInitializing] = useState(true);
   const { showBoundary: showErrorBoundary } = useErrorBoundary();
-  const loadingText = useSelector((state: State) => state.app.loadingText);
+  const stateLoadingText = useSelector((state: State) => state.app.loadingText);
+  const [loadingText = stateLoadingText, setLoadingText] = useState<
+    string | null
+  >(null);
   const { loadBudget, closeBudget, loadGlobalPrefs } = useActions();
 
   async function init() {
     const socketName = await global.Actual.getServerSocket();
 
+    setLoadingText('Initializing the connection to the local database...');
     await initConnection(socketName);
 
     // Load any global prefs
+    setLoadingText('Loading global preferences...');
     await loadGlobalPrefs();
 
     // Open the last opened budget, if any
+    setLoadingText('Opening last budget...');
     const budgetId = await send('get-last-opened-backup');
     if (budgetId) {
+      setLoadingText('Loading the last budget file...');
       await loadBudget(budgetId);
 
       // Check to see if this file has been remotely deleted (but
       // don't block on this in case they are offline or something)
+      setLoadingText('Retrieving remote files...');
       send('get-remote-files').then(files => {
         if (files) {
           const remoteFile = files.find(f => f.fileId === cloudFileId);
@@ -71,6 +79,7 @@ function AppInner({ budgetId, cloudFileId }: AppInnerProps) {
     async function initAll() {
       await Promise.all([installPolyfills(), init()]);
       setInitializing(false);
+      setLoadingText(null);
     }
 
     initAll().catch(showErrorBoundary);
diff --git a/packages/desktop-client/src/components/AppBackground.tsx b/packages/desktop-client/src/components/AppBackground.tsx
index dd484ac60c0c922b47839d26e2903d6554d5c3ec..daff02ad650f337c004ff1a71a8cb87488474e0f 100644
--- a/packages/desktop-client/src/components/AppBackground.tsx
+++ b/packages/desktop-client/src/components/AppBackground.tsx
@@ -1,4 +1,5 @@
 import React from 'react';
+import { useTransition, animated } from 'react-spring';
 
 import { css } from 'glamor';
 
@@ -18,29 +19,39 @@ export function AppBackground({
   initializing,
   loadingText,
 }: AppBackgroundProps) {
+  const transitions = useTransition(loadingText, {
+    from: { opacity: 0, transform: 'translateY(-100px)' },
+    enter: { opacity: 1, transform: 'translateY(0)' },
+    leave: { opacity: 0, transform: 'translateY(-100px)' },
+    unique: true,
+  });
+
   return (
     <>
       <Background />
 
-      {(loadingText != null || initializing) && (
-        <View
-          className={`${css({
-            position: 'absolute',
-            top: 0,
-            left: 0,
-            right: 0,
-            padding: 50,
-            paddingTop: 200,
-            color: theme.pageText,
-            alignItems: 'center',
-          })}`}
-        >
-          <Block style={{ marginBottom: 20, fontSize: 18 }}>
-            {loadingText}
-          </Block>
-          <AnimatedLoading width={25} color={theme.pageText} />
-        </View>
-      )}
+      {(loadingText != null || initializing) &&
+        transitions((style, item) => (
+          <animated.div key={item} style={style}>
+            <View
+              className={`${css({
+                position: 'absolute',
+                top: 0,
+                left: 0,
+                right: 0,
+                padding: 50,
+                paddingTop: 200,
+                color: theme.pageText,
+                alignItems: 'center',
+              })}`}
+            >
+              <Block style={{ marginBottom: 20, fontSize: 18 }}>
+                {loadingText}
+              </Block>
+              <AnimatedLoading width={25} color={theme.pageText} />
+            </View>
+          </animated.div>
+        ))}
     </>
   );
 }
diff --git a/packages/desktop-client/src/components/util/LoadComponent.tsx b/packages/desktop-client/src/components/util/LoadComponent.tsx
index a0769656e1b41f4eb9eb31a6623443b213e2ad45..9d4275ade509c89aaf13a8794f42fbbceb1cbccc 100644
--- a/packages/desktop-client/src/components/util/LoadComponent.tsx
+++ b/packages/desktop-client/src/components/util/LoadComponent.tsx
@@ -1,5 +1,7 @@
 import { type ComponentType, useEffect, useState } from 'react';
 
+import promiseRetry from 'promise-retry';
+
 import { AnimatedLoading } from '../../icons/AnimatedLoading';
 import { theme, styles } from '../../style';
 import { Block } from '../common/Block';
@@ -23,10 +25,29 @@ function LoadComponentInner<K extends string>({
   importer,
 }: LoadComponentProps<K>) {
   const [Component, setComponent] = useState<ProplessComponent | null>(null);
+  const [failedToLoad, setFailedToLoad] = useState(false);
+
   useEffect(() => {
-    importer().then(module => setComponent(() => module[name]));
+    setFailedToLoad(false);
+
+    // Load the module; if it fails - retry with exponential backoff
+    promiseRetry(
+      retry =>
+        importer()
+          .then(module => setComponent(() => module[name]))
+          .catch(retry),
+      {
+        retries: 5,
+      },
+    ).catch(() => {
+      setFailedToLoad(true);
+    });
   }, [name, importer]);
 
+  if (failedToLoad) {
+    throw new Error(`Failed loading the ${name} lazy module.`);
+  }
+
   if (!Component) {
     return (
       <View
@@ -46,8 +67,5 @@ function LoadComponentInner<K extends string>({
     );
   }
 
-  // console.log(
-  //   `rendering <${Component.displayName || Component.name} /> as ${name}`,
-  // );
   return <Component />;
 }
diff --git a/upcoming-release-notes/2639.md b/upcoming-release-notes/2639.md
new file mode 100644
index 0000000000000000000000000000000000000000..61c2ba5219ef3ba70de9b8d4e882d9ff3a1dd950
--- /dev/null
+++ b/upcoming-release-notes/2639.md
@@ -0,0 +1,6 @@
+---
+category: Enhancements
+authors: [MatissJanis]
+---
+
+Added app-loading stage description texts; also added exponential backoff in case a lazy-loaded module fails loading
diff --git a/yarn.lock b/yarn.lock
index 3016c10bd68d209181e4b58800bb8dcb5584e06e..1373b94432aabd5edaa15d685fa9e0af13264af2 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -73,6 +73,7 @@ __metadata:
     "@swc/plugin-react-remove-properties": "npm:^1.5.108"
     "@testing-library/react": "npm:14.1.2"
     "@testing-library/user-event": "npm:14.5.2"
+    "@types/promise-retry": "npm:^1.1.6"
     "@types/react": "npm:^18.2.0"
     "@types/react-dom": "npm:^18.2.1"
     "@types/react-modal": "npm:^3.16.0"
@@ -95,6 +96,7 @@ __metadata:
     mdast-util-newline-to-break: "npm:^2.0.0"
     memoize-one: "npm:^6.0.0"
     pikaday: "npm:1.8.2"
+    promise-retry: "npm:^2.0.1"
     react: "npm:18.2.0"
     react-aria-components: "npm:^1.1.1"
     react-dnd: "npm:^16.0.1"
@@ -5524,6 +5526,15 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@types/promise-retry@npm:^1.1.6":
+  version: 1.1.6
+  resolution: "@types/promise-retry@npm:1.1.6"
+  dependencies:
+    "@types/retry": "npm:*"
+  checksum: 8834adbc77399329fafa4e07ada57fc49007802f81af42cd74b2c51e89a3cdd9aa4c09a05e3909aaea9d165bd2ff940ed309db21c21c373104e479b11e24435c
+  languageName: node
+  linkType: hard
+
 "@types/prop-types@npm:*, @types/prop-types@npm:^15.0.0":
   version: 15.7.5
   resolution: "@types/prop-types@npm:15.7.5"
@@ -5590,6 +5601,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@types/retry@npm:*":
+  version: 0.12.5
+  resolution: "@types/retry@npm:0.12.5"
+  checksum: 3fb6bf91835ca0eb2987567d6977585235a7567f8aeb38b34a8bb7bbee57ac050ed6f04b9998cda29701b8c893f5dfe315869bc54ac17e536c9235637fe351a2
+  languageName: node
+  linkType: hard
+
 "@types/scheduler@npm:*":
   version: 0.16.3
   resolution: "@types/scheduler@npm:0.16.3"