From 5b49ea8ea4b3988d96f7d6eb335fdde1973301d0 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Sat, 21 Feb 2026 13:37:09 -0600 Subject: [PATCH] Scope Suspense to content area with skeleton fallback Moved the Suspense boundary from App.tsx (which wrapped the entire app) into MainLayout around just the Outlet and right sidebar. The left sidebar, mobile nav, and mobile top bar now persist while lazy page chunks load. A skeleton fallback fills the content area during loading. --- src/App.tsx | 6 ++-- src/components/MainLayout.tsx | 63 +++++++++++++++++++++++++++++++---- 2 files changed, 59 insertions(+), 10 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index b1de73a9..382c77fb 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -4,7 +4,7 @@ import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { createHead, UnheadProvider } from '@unhead/react/client'; import { InferSeoMetaPlugin } from '@unhead/addons'; -import { Suspense, useEffect } from 'react'; +import { useEffect } from 'react'; import NostrProvider from '@/components/NostrProvider'; import { NostrSync } from '@/components/NostrSync'; import { NativeNotifications } from '@/components/NativeNotifications'; @@ -99,9 +99,7 @@ export function App() { - - - + diff --git a/src/components/MainLayout.tsx b/src/components/MainLayout.tsx index e83b085b..05042dca 100644 --- a/src/components/MainLayout.tsx +++ b/src/components/MainLayout.tsx @@ -1,4 +1,4 @@ -import { useState, useMemo } from 'react'; +import { Suspense, useState, useMemo } from 'react'; import { Outlet } from 'react-router-dom'; import { LeftSidebar } from '@/components/LeftSidebar'; import { RightSidebar } from '@/components/RightSidebar'; @@ -6,9 +6,60 @@ import { MobileTopBar } from '@/components/MobileTopBar'; import { MobileBottomNav } from '@/components/MobileBottomNav'; import { MobileDrawer } from '@/components/MobileDrawer'; import { FloatingComposeButton } from '@/components/FloatingComposeButton'; +import { Skeleton } from '@/components/ui/skeleton'; import { LayoutStore, LayoutStoreContext, useLayoutSnapshot } from '@/contexts/LayoutContext'; import { cn } from '@/lib/utils'; +/** Skeleton shown in the content area while a lazy page chunk is loading. */ +function PageSkeleton() { + return ( + <> + {/* Main column skeleton */} +
+ {/* Header skeleton */} +
+ +
+ {/* Content skeletons */} +
+ {Array.from({ length: 4 }).map((_, i) => ( +
+
+ +
+ + +
+
+
+ + +
+
+ ))} +
+
+ {/* Right sidebar skeleton */} + + + ); +} + /** Inner component that reads layout options from the context store. */ function MainLayoutInner() { const { rightSidebar, showFAB = false, noBottomSpacer = false, wrapperClassName } = useLayoutSnapshot(); @@ -29,11 +80,11 @@ function MainLayoutInner() { - {/* Main content area - swapped by the router */} - - - {/* Desktop right sidebar - handled internally with hidden lg:block */} - {rightSidebar ?? } + {/* Main content + right sidebar: inside Suspense so the left sidebar persists while lazy pages load */} + }> + + {rightSidebar ?? } + {/* Mobile bottom nav - only on small screens */}