Skip to content

Commit cc2d84c

Browse files
committed
fix(sidebar): reconcile migrated-legacy collapse before paint
A user whose collapse lived only in localStorage has no sidebar_collapsed cookie at SSR (initialCollapsed=false), but the pre-paint script migrates them to a cookie. The store's persist.rehydrate() is async (flips _hasHydrated after paint), so the first paint showed expanded labels in the collapsed 51px rail. Reconcile to the cookie synchronously in a useLayoutEffect (first render still matches the server, so no hydration mismatch) — no narrow-rail flash.
1 parent af66d9f commit cc2d84c

1 file changed

Lines changed: 14 additions & 4 deletions

File tree

  • apps/sim/app/workspace/[workspaceId]/w/components/sidebar

apps/sim/app/workspace/[workspaceId]/w/components/sidebar/sidebar.tsx

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ import { SIDEBAR_WIDTH } from '@/stores/constants'
109109
import { useFolderStore } from '@/stores/folders/store'
110110
import { useSearchModalStore } from '@/stores/modals/search/store'
111111
import { useProvidersStore } from '@/stores/providers'
112-
import { useSidebarStore } from '@/stores/sidebar/store'
112+
import { readCollapsedCookie, useSidebarStore } from '@/stores/sidebar/store'
113113

114114
const logger = createLogger('Sidebar')
115115

@@ -397,10 +397,20 @@ export const Sidebar = memo(function Sidebar({ initialCollapsed = false }: Sideb
397397
/**
398398
* The server renders from the `sidebar_collapsed` cookie (via `initialCollapsed`)
399399
* and the client store seeds from the same cookie, so both agree on the first
400-
* paint. The prop is read until the store reports hydration to guarantee that
401-
* match, after which the store takes over.
400+
* paint. The prop is read until the store reports hydration, after which the
401+
* store takes over.
402+
*
403+
* A legacy user whose collapse lived only in `localStorage` has no cookie at SSR
404+
* (so `initialCollapsed` is false), but the pre-paint script migrates them to a
405+
* cookie. Reconcile to that cookie synchronously before paint — the first render
406+
* still matches the server, so there's no hydration mismatch and no narrow-rail flash.
402407
*/
403-
const isCollapsed = hasHydrated ? storeIsCollapsed : initialCollapsed
408+
const [migratedCollapsed, setMigratedCollapsed] = useState<boolean | null>(null)
409+
useLayoutEffect(() => {
410+
const cookieCollapsed = readCollapsedCookie()
411+
if (cookieCollapsed !== initialCollapsed) setMigratedCollapsed(cookieCollapsed)
412+
}, [initialCollapsed])
413+
const isCollapsed = hasHydrated ? storeIsCollapsed : (migratedCollapsed ?? initialCollapsed)
404414

405415
/**
406416
* Hydrates the persisted width before paint (collapse already came from the

0 commit comments

Comments
 (0)