blumenjs 0.2.3 → 0.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -212,6 +212,14 @@ export function RouterProvider({
212
212
  return () => window.removeEventListener("popstate", onPopState);
213
213
  }, []);
214
214
 
215
+ // Track whether hydration is complete. During hydration, we skip
216
+ // Suspense because the lazy chunk hasn't loaded yet and Suspense
217
+ // would show a fallback that mismatches the server HTML.
218
+ const [hydrated, setHydrated] = useState(false);
219
+ useEffect(() => {
220
+ setHydrated(true);
221
+ }, []);
222
+
215
223
  // ── Render ──────────────────────────────────────────────────
216
224
  const contextValue: RouterContextValue = { path, params, navigate };
217
225
 
@@ -220,6 +228,19 @@ export function RouterProvider({
220
228
  // 2. Otherwise — show the page component
221
229
  const LoadingComp = loadingComponent;
222
230
 
231
+ // During initial hydration, render App directly (no Suspense/ErrorBoundary)
232
+ // to match the SSR output exactly. After hydration, wrap with Suspense
233
+ // for lazy-loaded navigation.
234
+ const pageContent = hydrated ? (
235
+ <BlumenErrorBoundary>
236
+ <Suspense fallback={LoadingComp ? <LoadingComp /> : <DefaultLoading />}>
237
+ <App Component={PageComponent} pageProps={pageProps} />
238
+ </Suspense>
239
+ </BlumenErrorBoundary>
240
+ ) : (
241
+ <App Component={PageComponent} pageProps={pageProps} />
242
+ );
243
+
223
244
  return (
224
245
  <RouterContext.Provider value={contextValue}>
225
246
  {isLoading && LoadingComp ? (
@@ -230,11 +251,7 @@ export function RouterProvider({
230
251
  <div
231
252
  className={`page-transition ${transitioning ? "page-transition-exit" : "page-transition-active"}`}
232
253
  >
233
- <BlumenErrorBoundary>
234
- <Suspense fallback={LoadingComp ? <LoadingComp /> : <DefaultLoading />}>
235
- <App Component={PageComponent} pageProps={pageProps} />
236
- </Suspense>
237
- </BlumenErrorBoundary>
254
+ {pageContent}
238
255
  </div>
239
256
  )}
240
257
  </RouterContext.Provider>
@@ -342,8 +342,9 @@ async function handleRender(
342
342
  };
343
343
 
344
344
  // Render React to HTML
345
- // Wrap in the same page-transition div that the client-side
346
- // RouterProvider renders, so hydration trees match exactly.
345
+ // Must match the client's INITIAL hydration tree exactly:
346
+ // div.page-transition > App
347
+ // (Client adds Suspense/ErrorBoundary AFTER hydration via useEffect)
347
348
  const appElement = React.createElement(
348
349
  "div",
349
350
  { className: "page-transition page-transition-active" },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blumenjs",
3
- "version": "0.2.3",
3
+ "version": "0.2.4",
4
4
  "description": "The React framework powered by Go. Lightning-fast SSR with the DX you deserve.",
5
5
  "type": "module",
6
6
  "bin": {