{props.title}
+{params.slug}
+ Post b +From e7356150d89855ea6682ac4beb00df2be44473fe Mon Sep 17 00:00:00 2001
From: yujiym <1409395+yujiym@users.noreply.github.com>
Date: Wed, 4 Nov 2026 23:27:27 +0900
Subject: [PATCH] fix(solid): keep useParams() current after client navigation
The generated client passed the params to ParamsContext.Provider through
a getter over the routeParams signal. Solid's provider reads its value
once, inside untrack, so every useParams() call received the first
page's params, even on pages created after a navigation.
Update a stable params object in place, the same way shared data is
updated, so the object returned by useParams() stays reactive and
current.
Fixes #708
---
packages/solid/src/plugin-pages.ts | 24 ++--
.../integration/use-params-navigation.test.ts | 107 ++++++++++++++++++
2 files changed, 121 insertions(+), 10 deletions(-)
create mode 100644 packages/solid/test/integration/use-params-navigation.test.ts
diff --git a/packages/solid/src/plugin-pages.ts b/packages/solid/src/plugin-pages.ts
index c07e942ec..47fb2a3d3 100644
--- a/packages/solid/src/plugin-pages.ts
+++ b/packages/solid/src/plugin-pages.ts
@@ -153,15 +153,17 @@ const [page, setPage] = createSignal({ component: null });
const [pageProps, setPageProps] = createSignal({});
const [layouts, setLayouts] = createSignal([]);
const shared = createMutable({});
+const params = createMutable({});
const [errors, setErrors] = createSignal({});
const [routeUrl, setRouteUrl] = createSignal(window.location.pathname + window.location.search + window.location.hash);
const [routeParams, setRouteParams] = createSignal({});
const [navigation, setNavigation] = createSignal(idleNavigationState());
-// Keep the context object stable for layouts that call useShared() once.
-function replaceShared(next) {
- for (const key of Object.keys(shared)) delete shared[key];
- Object.assign(shared, next || {});
+// Pages and layouts keep the objects returned by useShared() and useParams()
+// across navigations, so update them in place.
+function replaceInPlace(target, next) {
+ for (const key of Object.keys(target)) delete target[key];
+ Object.assign(target, next || {});
}
function sameParams(a, b) {
@@ -177,18 +179,19 @@ const { router, initialPageData, initialComponent, initialLayouts, start, commit
rejectDeferred(ref, error) { return { value: null, loading: false, error: new Error(error) }; },
isLoading: (ref) => ref.loading,
applyUpdate(pageData, comp, layoutComps, options) {
- const params = pageData.params || {};
+ const nextParams = pageData.params || {};
// Back/forward preserves state, but another route or another record
// still needs a new page so useForm() targets that route and record.
const current = page();
- const keep = options?.preserveState && current.id === pageData.component && current.component === comp && sameParams(routeParams(), params);
+ const keep = options?.preserveState && current.id === pageData.component && current.component === comp && sameParams(routeParams(), nextParams);
batch(() => {
if (!keep) setPage({ component: comp, id: pageData.component });
setPageProps(pageData.props);
setLayouts(layoutComps);
- replaceShared(pageData.shared);
+ replaceInPlace(shared, pageData.shared);
setErrors(pageData.errors || {});
- setRouteParams(params);
+ replaceInPlace(params, nextParams);
+ setRouteParams(nextParams);
});
},
onDeferredUpdate(props) {
@@ -218,8 +221,9 @@ batch(() => {
setPage({ component: initialComponent, id: initialPageData.component });
setPageProps(initialPageData.props);
setLayouts(initialLayouts);
- replaceShared(initialPageData.shared);
+ replaceInPlace(shared, initialPageData.shared);
setErrors(initialPageData.errors || {});
+ replaceInPlace(params, initialPageData.params);
setRouteParams(initialPageData.params || {});
});
@@ -254,7 +258,7 @@ function App() {
value: reactiveRouter,
get children() {
return createComponent(ParamsContext.Provider, {
- get value() { return routeParams(); },
+ value: params,
get children() {
return createComponent(NavigationContext.Provider, {
value: navigation,
diff --git a/packages/solid/test/integration/use-params-navigation.test.ts b/packages/solid/test/integration/use-params-navigation.test.ts
new file mode 100644
index 000000000..4b5258cb3
--- /dev/null
+++ b/packages/solid/test/integration/use-params-navigation.test.ts
@@ -0,0 +1,107 @@
+import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
+import { join } from 'node:path';
+import { chromium } from 'playwright';
+import { createServer } from 'vite';
+import solid from 'vite-plugin-solid';
+import { expect, it } from 'vite-plus/test';
+import { pagesPlugin } from '../../src/plugin-pages.ts';
+
+const PAGE = `import { Link, useParams } from "@void/solid";
+export default function Post(props) {
+ const params = useParams();
+ return {params.slug}{props.title}
+
{params.slug}
{props.children}