diff --git a/.changeset/tender-olives-clean.md b/.changeset/tender-olives-clean.md
new file mode 100644
index 0000000000..1923b6bdc0
--- /dev/null
+++ b/.changeset/tender-olives-clean.md
@@ -0,0 +1,5 @@
+---
+'@backstage/frontend-defaults': patch
+---
+
+**BREAKING**: The `loadingComponent` option has been renamed to `loadingElement`, which is now found under `advanced.loadingElement`. The default loading element has also been switched to `` from `@backstage/core-components`. This is of course an improvement over the previous `"Loading..."` text, but also helps prevent flicker when the app loading is fast.
diff --git a/packages/frontend-defaults/package.json b/packages/frontend-defaults/package.json
index 485f0f3497..5d4fe573cc 100644
--- a/packages/frontend-defaults/package.json
+++ b/packages/frontend-defaults/package.json
@@ -32,6 +32,7 @@
},
"dependencies": {
"@backstage/config": "workspace:^",
+ "@backstage/core-components": "workspace:^",
"@backstage/errors": "workspace:^",
"@backstage/frontend-app-api": "workspace:^",
"@backstage/frontend-plugin-api": "workspace:^",
diff --git a/packages/frontend-defaults/report.api.md b/packages/frontend-defaults/report.api.md
index 8042345d35..6fd2536251 100644
--- a/packages/frontend-defaults/report.api.md
+++ b/packages/frontend-defaults/report.api.md
@@ -28,7 +28,7 @@ export interface CreateAppOptions {
extensionFactoryMiddleware?:
| ExtensionFactoryMiddleware
| ExtensionFactoryMiddleware[];
- loadingComponent?: ReactNode;
+ loadingElement?: ReactNode;
pluginInfoResolver?: FrontendPluginInfoResolver;
};
bindRoutes?(context: { bind: CreateAppRouteBinder }): void;
diff --git a/packages/frontend-defaults/src/createApp.test.tsx b/packages/frontend-defaults/src/createApp.test.tsx
index 4200cf92b8..7faef3a8a8 100644
--- a/packages/frontend-defaults/src/createApp.test.tsx
+++ b/packages/frontend-defaults/src/createApp.test.tsx
@@ -427,7 +427,7 @@ describe('createApp', () => {
`);
});
- it('should use "Loading..." as the default suspense fallback', async () => {
+ it('should use as the default suspense fallback', async () => {
const app = createApp({
advanced: {
configLoader: () => new Promise(() => {}),
@@ -436,33 +436,33 @@ describe('createApp', () => {
await renderWithEffects(app.createRoot());
- await expect(screen.findByText('Loading...')).resolves.toBeInTheDocument();
+ await expect(screen.findByTestId('progress')).resolves.toBeInTheDocument();
});
- it('should use no suspense fallback if the "loadingComponent" is null', async () => {
+ it('should use no suspense fallback if the loadingElement is null', async () => {
const app = createApp({
advanced: {
configLoader: () => new Promise(() => {}),
- loadingComponent: null,
+ loadingElement: null,
},
});
await renderWithEffects(app.createRoot());
- expect(screen.queryByText('Loading...')).toBeNull();
+ expect(screen.queryByTestId('progress')).toBeNull();
});
- it('should use a custom "loadingComponent"', async () => {
+ it('should use a custom loadingElement', async () => {
const app = createApp({
advanced: {
configLoader: () => new Promise(() => {}),
- loadingComponent: "Custom loading message",
+ loadingElement: Custom loading message,
},
});
await renderWithEffects(app.createRoot());
- expect(screen.queryByText('Custom loading message')).toBeNull();
+ expect(screen.queryByText('Custom loading message')).toBeInTheDocument();
});
it('should allow overriding the app plugin', async () => {
diff --git a/packages/frontend-defaults/src/createApp.tsx b/packages/frontend-defaults/src/createApp.tsx
index 828fbd4622..2c8a80791f 100644
--- a/packages/frontend-defaults/src/createApp.tsx
+++ b/packages/frontend-defaults/src/createApp.tsx
@@ -22,6 +22,7 @@ import {
FrontendFeature,
FrontendFeatureLoader,
} from '@backstage/frontend-plugin-api';
+import { Progress } from '@backstage/core-components';
// eslint-disable-next-line @backstage/no-relative-monorepo-imports
import { defaultConfigLoaderSync } from '../../core-app-api/src/app/defaultConfigLoader';
// eslint-disable-next-line @backstage/no-relative-monorepo-imports
@@ -87,13 +88,12 @@ export interface CreateAppOptions {
| ExtensionFactoryMiddleware[];
/**
- * The component to render while loading the app (waiting for config,
- * features, etc).
+ * The element to render while loading the app (waiting for config, features, etc).
*
- * This is the text "Loading..." by default. If set to "null" then no loading
- * fallback component is rendered at all.
+ * This is the `` component from `@backstage/core-components` by default.
+ * If set to `null` then no loading fallback element is rendered at all.
*/
- loadingComponent?: ReactNode;
+ loadingElement?: ReactNode;
/**
* Allows for customizing how plugin info is retrieved.
@@ -110,9 +110,9 @@ export interface CreateAppOptions {
export function createApp(options?: CreateAppOptions): {
createRoot(): JSX.Element;
} {
- let suspenseFallback = options?.advanced?.loadingComponent;
+ let suspenseFallback = options?.advanced?.loadingElement;
if (suspenseFallback === undefined) {
- suspenseFallback = 'Loading...';
+ suspenseFallback = ;
}
async function appLoader() {
@@ -143,9 +143,10 @@ export function createApp(options?: CreateAppOptions): {
return { default: () => rootEl };
}
+ const LazyApp = lazy(appLoader);
+
return {
createRoot() {
- const LazyApp = lazy(appLoader);
return (
diff --git a/yarn.lock b/yarn.lock
index b15db89924..c3b14a7921 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3489,6 +3489,7 @@ __metadata:
dependencies:
"@backstage/cli": "workspace:^"
"@backstage/config": "workspace:^"
+ "@backstage/core-components": "workspace:^"
"@backstage/core-plugin-api": "workspace:^"
"@backstage/errors": "workspace:^"
"@backstage/frontend-app-api": "workspace:^"