diff --git a/frontend/docs/list_detail_pages.md b/frontend/docs/list_detail_pages.md
index 6002e095..17be4fb3 100644
--- a/frontend/docs/list_detail_pages.md
+++ b/frontend/docs/list_detail_pages.md
@@ -466,8 +466,13 @@ export function EntitiesPage() {
const pageHeader = ;
- // Canonical 4-branch render gate — pageHeader renders in ALL branches:
- if (isLoading) {
+ // Canonical 4-branch render gate — pageHeader renders in ALL branches.
+ // Both loading and error gate on "nothing to show yet": the query is
+ // cache-and-network, so a background revalidation reports loading (and, on
+ // failure, error) with cached data still present. Without `&& !data` that
+ // refetch blanks a working list — or an edit form with unsaved changes —
+ // with the spinner/error for the round-trip.
+ if (isLoading && entities.length === 0) {
return (
<>
{pageHeader}
@@ -475,8 +480,6 @@ export function EntitiesPage() {
>
);
}
- // Show the error surface only when there's no data — a failed background
- // refetch must not blank a working list.
if (error && entities.length === 0) {
return (
<>
diff --git a/frontend/src/pages/settings/settings-prompt.tsx b/frontend/src/pages/settings/settings-prompt.tsx
index 0c5d2ebf..02a74c1d 100644
--- a/frontend/src/pages/settings/settings-prompt.tsx
+++ b/frontend/src/pages/settings/settings-prompt.tsx
@@ -727,7 +727,7 @@ function SettingsPrompt() {
);
- if (loading) {
+ if (loading && !data) {
return (
<>
{pageHeader}
diff --git a/frontend/src/pages/settings/settings-provider.test.tsx b/frontend/src/pages/settings/settings-provider.test.tsx
index fe631325..4ce76951 100644
--- a/frontend/src/pages/settings/settings-provider.test.tsx
+++ b/frontend/src/pages/settings/settings-provider.test.tsx
@@ -137,4 +137,15 @@ describe('SettingsProvider create-form type guards', () => {
expect(screen.queryByText('Error loading provider data')).not.toBeInTheDocument();
expect(navigate).not.toHaveBeenCalled();
});
+
+ // The loading branch runs before the error branch, so it needs the same guard: a background
+ // refetch reports loading:true with cached data and must not blank the form to the spinner.
+ it('keeps the form on a background refetch while cached data is present', () => {
+ setSearch('type=anthropic');
+ queryResult.loading = true;
+ render();
+
+ expect(screen.queryByText('Loading provider data...')).not.toBeInTheDocument();
+ expect(navigate).not.toHaveBeenCalled();
+ });
});
diff --git a/frontend/src/pages/settings/settings-provider.tsx b/frontend/src/pages/settings/settings-provider.tsx
index e985350e..1c7a0ca1 100644
--- a/frontend/src/pages/settings/settings-provider.tsx
+++ b/frontend/src/pages/settings/settings-provider.tsx
@@ -1605,7 +1605,7 @@ function SettingsProvider() {
}
};
- if (loading) {
+ if (loading && !data) {
return (
<>
diff --git a/frontend/src/pages/templates/template.tsx b/frontend/src/pages/templates/template.tsx
index b2211ee1..93c90e08 100644
--- a/frontend/src/pages/templates/template.tsx
+++ b/frontend/src/pages/templates/template.tsx
@@ -734,7 +734,7 @@ function Template() {
/>
);
- if (!isNew && isLoadingTemplate) {
+ if (!isNew && isLoadingTemplate && !template) {
return (
{pageHeader}