diff --git a/packages/example-pathless-ssr/package.json b/packages/example-pathless-ssr/package.json
index af667fb..618fc0f 100644
--- a/packages/example-pathless-ssr/package.json
+++ b/packages/example-pathless-ssr/package.json
@@ -12,8 +12,8 @@
"dependencies": {
"@funstack/router": "workspace:*",
"@funstack/static": "^1.2.0",
- "react": "19.3.0-canary-1b45e243-20260402",
- "react-dom": "19.3.0-canary-1b45e243-20260402"
+ "react": "^19.2.5",
+ "react-dom": "^19.2.5"
},
"devDependencies": {
"@types/react": "^19.2.14",
diff --git a/packages/example-pathless-ssr/src/ClientApp.tsx b/packages/example-pathless-ssr/src/ClientApp.tsx
index 829e820..928d53d 100644
--- a/packages/example-pathless-ssr/src/ClientApp.tsx
+++ b/packages/example-pathless-ssr/src/ClientApp.tsx
@@ -6,7 +6,5 @@ import "./styles.css";
export function ClientApp({ routes }: { routes: RouteDefinition[] }) {
// No ssr prop — during SSR only pathless routes match, rendering the app shell.
// Path-based content fills in on client hydration.
- // experimentalPostpone uses React's unstable_postpone API to avoid hydration
- // mismatches by deferring unmatched outlet content to client rendering.
- return ;
+ return ;
}
diff --git a/packages/example-pathless-ssr/vite.config.ts b/packages/example-pathless-ssr/vite.config.ts
index 92788ac..877736a 100644
--- a/packages/example-pathless-ssr/vite.config.ts
+++ b/packages/example-pathless-ssr/vite.config.ts
@@ -11,10 +11,4 @@ export default defineConfig({
react(),
],
base: "/",
- resolve: {
- // Ensure all react imports resolve to the same instance.
- // Without this, the workspace router package may resolve to a different
- // React version than the one used by this example (React Canary).
- dedupe: ["react", "react-dom"],
- },
});
diff --git a/packages/router/src/Outlet.tsx b/packages/router/src/Outlet.tsx
index b5576c5..669fc61 100644
--- a/packages/router/src/Outlet.tsx
+++ b/packages/router/src/Outlet.tsx
@@ -1,40 +1,16 @@
import { type ReactNode, useContext } from "react";
-import * as React from "react";
import { RouteContext } from "./context/RouteContext.js";
-import { RouterContext } from "./context/RouterContext.js";
/**
* Renders the matched child route.
* Used in layout components to specify where child routes should render.
- *
- * When `experimentalPostpone` is enabled on the Router and no child route
- * matches during pathless SSR, calls `React.unstable_postpone()` instead of
- * rendering `null`, deferring the content to client-side rendering.
*/
export function Outlet(): ReactNode {
const routeContext = useContext(RouteContext);
- const routerContext = useContext(RouterContext);
if (!routeContext) {
return null;
}
- if (
- routeContext.outlet === null &&
- routerContext !== null &&
- routerContext.experimentalPostpone &&
- routerContext.url === null
- ) {
- // During pathless SSR, child routes cannot match because there is no URL.
- // Use React's experimental postpone API to signal that this content
- // should be rendered on the client instead.
- const postpone = (React as Record)["unstable_postpone"];
- if (typeof postpone === "function") {
- (postpone as (reason: string) => never)(
- "Route not matched during pathless SSR",
- );
- }
- }
-
return routeContext.outlet;
}
diff --git a/packages/router/src/Router/index.tsx b/packages/router/src/Router/index.tsx
index 853f27a..8273477 100644
--- a/packages/router/src/Router/index.tsx
+++ b/packages/router/src/Router/index.tsx
@@ -98,19 +98,6 @@ export type RouterProps = {
* ```
*/
ssr?: SSRConfig;
- /**
- * Enable React's experimental `unstable_postpone` API for pathless SSR.
- *
- * When enabled, `` calls `React.unstable_postpone()` instead of
- * rendering `null` when no child route can be matched during pathless SSR.
- * This avoids hydration mismatches by telling React to defer rendering of
- * the outlet content to the client.
- *
- * **Requires React Canary or experimental builds.**
- *
- * @default false
- */
- experimentalPostpone?: boolean;
};
export function Router({
@@ -118,7 +105,6 @@ export function Router({
onNavigate,
fallback = "none",
ssr,
- experimentalPostpone = false,
}: RouterProps): ReactNode {
const routes = internalRoutes(inputRoutes);
@@ -299,7 +285,6 @@ export function Router({
isPending,
navigateAsync,
updateCurrentEntryState,
- experimentalPostpone,
}),
[
locationState,
@@ -310,7 +295,6 @@ export function Router({
isPending,
navigateAsync,
updateCurrentEntryState,
- experimentalPostpone,
],
);
diff --git a/packages/router/src/context/RouterContext.ts b/packages/router/src/context/RouterContext.ts
index c8164f4..f41286f 100644
--- a/packages/router/src/context/RouterContext.ts
+++ b/packages/router/src/context/RouterContext.ts
@@ -24,11 +24,6 @@ export type RouterContextValue = {
navigateAsync: (to: string, options?: NavigateOptions) => Promise;
/** Update current entry's state without navigation */
updateCurrentEntryState: (state: unknown) => void;
- /**
- * Whether to use React's experimental `unstable_postpone` API for
- * content that cannot be rendered during pathless SSR.
- */
- experimentalPostpone: boolean;
};
export const RouterContext = createContext(null);
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 17e4087..6583b7a 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -90,13 +90,13 @@ importers:
version: link:../router
'@funstack/static':
specifier: ^1.2.0
- version: 1.2.0(react-dom@19.3.0-canary-1b45e243-20260402(react@19.3.0-canary-1b45e243-20260402))(react@19.3.0-canary-1b45e243-20260402)(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.4))
+ version: 1.2.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.4))
react:
- specifier: 19.3.0-canary-1b45e243-20260402
- version: 19.3.0-canary-1b45e243-20260402
+ specifier: ^19.2.5
+ version: 19.2.5
react-dom:
- specifier: 19.3.0-canary-1b45e243-20260402
- version: 19.3.0-canary-1b45e243-20260402(react@19.3.0-canary-1b45e243-20260402)
+ specifier: ^19.2.5
+ version: 19.2.5(react@19.2.5)
devDependencies:
'@types/react':
specifier: ^19.2.14
@@ -1632,11 +1632,6 @@ packages:
peerDependencies:
react: ^19.2.5
- react-dom@19.3.0-canary-1b45e243-20260402:
- resolution: {integrity: sha512-GUAjHZZGLPaq0oWL8JAT/Aeal/Urbk6mrzvvhxAmcT0xJkiZnMKkiWNFm4AV84tmgrHmKQzCTxfIs9LG5P228A==}
- peerDependencies:
- react: 19.3.0-canary-1b45e243-20260402
-
react-error-boundary@6.1.1:
resolution: {integrity: sha512-BrYwPOdXi5mqkk5lw+Uvt0ThHx32rCt3BkukS4X23A2AIWDPSGX6iaWTc0y9TU/mHDA/6qOSGel+B2ERkOvD1w==}
peerDependencies:
@@ -1649,10 +1644,6 @@ packages:
resolution: {integrity: sha512-llUJLzz1zTUBrskt2pwZgLq59AemifIftw4aB7JxOqf1HY2FDaGDxgwpAPVzHU1kdWabH7FauP4i1oEeer2WCA==}
engines: {node: '>=0.10.0'}
- react@19.3.0-canary-1b45e243-20260402:
- resolution: {integrity: sha512-CUTHdpLPSvcJovRhEegguvA6z5d76SoR/CXY32EnDoB22ScDV/PYjvKzAmxwRooY2VcZHzpD3WfcSgHRjocbPA==}
- engines: {node: '>=0.10.0'}
-
redent@3.0.0:
resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==}
engines: {node: '>=8'}
@@ -1707,9 +1698,6 @@ packages:
scheduler@0.27.0:
resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==}
- scheduler@0.28.0-canary-1b45e243-20260402:
- resolution: {integrity: sha512-9tsooPtKSPIsXs2Z4v3/QzR30fRDMlJncEqbb/TAadkfT54lKGbZuTpuNIt1N68IEhq5y4dxvaqC1pTYOYwA9Q==}
-
semver@7.7.4:
resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==}
engines: {node: '>=10'}
@@ -2350,19 +2338,6 @@ snapshots:
transitivePeerDependencies:
- react-server-dom-webpack
- '@funstack/static@1.2.0(react-dom@19.3.0-canary-1b45e243-20260402(react@19.3.0-canary-1b45e243-20260402))(react@19.3.0-canary-1b45e243-20260402)(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.4))':
- dependencies:
- '@funstack/skill-installer': 1.0.0
- '@vitejs/plugin-rsc': 0.5.23(react-dom@19.3.0-canary-1b45e243-20260402(react@19.3.0-canary-1b45e243-20260402))(react@19.3.0-canary-1b45e243-20260402)(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.4))
- react: 19.3.0-canary-1b45e243-20260402
- react-dom: 19.3.0-canary-1b45e243-20260402(react@19.3.0-canary-1b45e243-20260402)
- react-error-boundary: 6.1.1(react@19.3.0-canary-1b45e243-20260402)
- rsc-html-stream: 0.0.7
- srvx: 0.11.15
- vite: 8.0.10(@types/node@25.6.0)(esbuild@0.27.4)
- transitivePeerDependencies:
- - react-server-dom-webpack
-
'@img/colour@1.1.0': {}
'@img/sharp-darwin-arm64@0.34.5':
@@ -2770,20 +2745,6 @@ snapshots:
vite: 8.0.10(@types/node@25.6.0)(esbuild@0.27.4)
vitefu: 1.1.3(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.4))
- '@vitejs/plugin-rsc@0.5.23(react-dom@19.3.0-canary-1b45e243-20260402(react@19.3.0-canary-1b45e243-20260402))(react@19.3.0-canary-1b45e243-20260402)(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.4))':
- dependencies:
- '@rolldown/pluginutils': 1.0.0-rc.13
- es-module-lexer: 2.0.0
- estree-walker: 3.0.3
- magic-string: 0.30.21
- react: 19.3.0-canary-1b45e243-20260402
- react-dom: 19.3.0-canary-1b45e243-20260402(react@19.3.0-canary-1b45e243-20260402)
- srvx: 0.11.15
- strip-literal: 3.1.0
- turbo-stream: 3.2.0
- vite: 8.0.10(@types/node@25.6.0)(esbuild@0.27.4)
- vitefu: 1.1.3(vite@8.0.10(@types/node@25.6.0)(esbuild@0.27.4))
-
'@vitest/expect@4.1.5':
dependencies:
'@standard-schema/spec': 1.1.0
@@ -3228,25 +3189,14 @@ snapshots:
react: 19.2.5
scheduler: 0.27.0
- react-dom@19.3.0-canary-1b45e243-20260402(react@19.3.0-canary-1b45e243-20260402):
- dependencies:
- react: 19.3.0-canary-1b45e243-20260402
- scheduler: 0.28.0-canary-1b45e243-20260402
-
react-error-boundary@6.1.1(react@19.2.5):
dependencies:
react: 19.2.5
- react-error-boundary@6.1.1(react@19.3.0-canary-1b45e243-20260402):
- dependencies:
- react: 19.3.0-canary-1b45e243-20260402
-
react-is@17.0.2: {}
react@19.2.5: {}
- react@19.3.0-canary-1b45e243-20260402: {}
-
redent@3.0.0:
dependencies:
indent-string: 4.0.0
@@ -3313,8 +3263,6 @@ snapshots:
scheduler@0.27.0: {}
- scheduler@0.28.0-canary-1b45e243-20260402: {}
-
semver@7.7.4: {}
sharp@0.34.5: