You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
After switching to another workspace via the workspace switcher, the sidebar shows the spaces (e.g. "General", "Shared") but no pages underneath. The space looks empty. A browser reload (F5) makes the pages appear. Clicking on the space name after the switch also shows them.
So the pages are there – the space is simply collapsed, and a collapsed space is visually indistinguishable from an empty one. Users who work on the mobile app and then open the web app to continue editing conclude that their pages did not sync (that is what happened to me for weeks).
Two things combine to cause this:
The sidebar expand state is one global localStorage entry for all workspaces and gets wiped on every workspace switch. src/components/_shared/outline/utils.ts:181 stores expanded view ids in a single key outline_expanded, not scoped by workspace. src/components/app/outline/Outline.tsx:351-400 (the "validate restored expanded IDs" effect) then checks every restored id against the current workspace via GET /api/workspace/<ws>/views?depth=1&view_ids=…. Ids that belong to another workspace obviously come back empty and are treated as stale → setOutlineExpands(id, false) deletes them from localStorage.
Net effect: visiting workspace A erases the expand state of workspaces B, C, D… With several workspaces, the target workspace's spaces are therefore practically always collapsed after a switch.
After a switch, nothing auto-expands the space that contains the selected page.
The switch navigates to the workspace's last-opened page. That page is already contained in the deep outline (/view/<ws>?depth=6), so the navigation-hydration effect in Outline.tsx:155 (if (findView(outline, selectedViewId)) return;) does not run and the ancestors are never expanded.
On a full reload the same effect fires while the outline is still [] (truthy), calls /view/<page>/navigation?depth=0, receives the ancestor ids and expands the space – which is why F5 "fixes" it.
To Reproduce
Self-hosted AppFlowy Cloud (docker compose, all images :latest, appflowy_web build 0.17.1 / image created 2026-08-31; behaviour identical to current main), account with several workspaces, each with pages under "General"
Open workspace A in the browser, expand "General" (pages visible)
Switch to workspace B via the switcher → "General" shows without any pages
Switch back to A → "General" is collapsed again, although it was expanded in step 2
Server-side evidence from the nginx access log for step 3 (workspace ids shortened):
PUT /api/workspace/B/open
GET /api/workspace/B/view/B?depth=6 200 6088 <- full outline incl. pages
GET /api/workspace/B/views?depth=1&view_ids=<space-of-A> 200 76 <- validating A's expanded space in B → empty → pruned
(no /navigation request)
and for the reload in step 4:
GET /api/workspace/B/view/<page>/navigation?depth=0 200 4849 <- ancestors → space expanded
GET /api/workspace/B/view/<space-of-B>?depth=1 200 4868
Expected behavior
Expand state should be scoped per workspace (e.g. outline_expanded_<workspaceId>), or the pruning should only remove ids that belonged to the current workspace.
After switching workspaces the space containing the selected page should be expanded (same behaviour as after a reload).
A collapsed space with children should be distinguishable from an empty one (chevron / count / placeholder).
Environment
Self-hosted, Docker Compose, :latest tags (appflowy_cloud, appflowy_web, gotrue, admin_frontend, appflowy_worker, ai, appflowy_search), Raspberry Pi ARM64, accessed via Tailscale (HTTPS)
Describe the bug
After switching to another workspace via the workspace switcher, the sidebar shows the spaces (e.g. "General", "Shared") but no pages underneath. The space looks empty. A browser reload (F5) makes the pages appear. Clicking on the space name after the switch also shows them.
So the pages are there – the space is simply collapsed, and a collapsed space is visually indistinguishable from an empty one. Users who work on the mobile app and then open the web app to continue editing conclude that their pages did not sync (that is what happened to me for weeks).
Two things combine to cause this:
The sidebar expand state is one global localStorage entry for all workspaces and gets wiped on every workspace switch.
src/components/_shared/outline/utils.ts:181stores expanded view ids in a single keyoutline_expanded, not scoped by workspace.src/components/app/outline/Outline.tsx:351-400(the "validate restored expanded IDs" effect) then checks every restored id against the current workspace viaGET /api/workspace/<ws>/views?depth=1&view_ids=…. Ids that belong to another workspace obviously come back empty and are treated as stale →setOutlineExpands(id, false)deletes them from localStorage.Net effect: visiting workspace A erases the expand state of workspaces B, C, D… With several workspaces, the target workspace's spaces are therefore practically always collapsed after a switch.
After a switch, nothing auto-expands the space that contains the selected page.
The switch navigates to the workspace's last-opened page. That page is already contained in the deep outline (
/view/<ws>?depth=6), so the navigation-hydration effect inOutline.tsx:155(if (findView(outline, selectedViewId)) return;) does not run and the ancestors are never expanded.On a full reload the same effect fires while the outline is still
[](truthy), calls/view/<page>/navigation?depth=0, receives the ancestor ids and expands the space – which is why F5 "fixes" it.To Reproduce
:latest,appflowy_webbuild 0.17.1 / image created 2026-08-31; behaviour identical to currentmain), account with several workspaces, each with pages under "General"Server-side evidence from the nginx access log for step 3 (workspace ids shortened):
and for the reload in step 4:
Expected behavior
outline_expanded_<workspaceId>), or the pruning should only remove ids that belonged to the current workspace.Environment
:latesttags (appflowy_cloud, appflowy_web, gotrue, admin_frontend, appflowy_worker, ai, appflowy_search), Raspberry Pi ARM64, accessed via Tailscale (HTTPS)