Skip to content

feat: keep fs-route params live for Client Components and expose route objects - #176

Merged
uhyo merged 2 commits into
masterfrom
claude/issue-174-route-prop-1nr7v4
Aug 27, 2026
Merged

feat: keep fs-route params live for Client Components and expose route objects#176
uhyo merged 2 commits into
masterfrom
claude/issue-174-route-prop-1nr7v4

Conversation

@uhyo

@uhyo uhyo commented Aug 26, 2026

Copy link
Copy Markdown
Owner

Summary

Addresses the client-component half of #174 (kept open for the remaining Server Component half).

Under file-system routing, every route component was baked into the RSC payload as a pre-rendered element, so dynamic params were frozen at the values of the initially-loaded page: soft client-side navigation between pages of the same dynamic route (e.g. /ja/en under [lang]) kept showing the old params everywhere, with no supported way to read the live ones.

Two changes:

  • Client Components get live params. A "use client" page or layout is now passed to FUNSTACK Router as a component type instead of a pre-rendered element. Client references serialize through the RSC boundary, so the router renders them in the browser with the params of the current match — their params prop stays live across soft navigation (along with the router's other route component props). No user code changes needed.
  • Route objects for everything else. Every generated route definition now carries a stable id, and Server Component pages/layouts receive a route prop: an opaque route object that can be forwarded to Client Components and given to the router's typed hooks (useRouteParams(route)) to read the live params of the URL currently shown. New types FsRouteObject<Params> and FsRouteComponentProps<Params> are exported from @funstack/static/fs-routes.

A Server Component page's own rendered output still reflects its build-time params after soft navigation: static hosting serves one pre-rendered payload per page and nothing can re-run a Server Component in the browser. That half of #174 needs on-navigation payload fetching and stays open.

Changes

  • fs-routes/runtime.tsx: pass client references as component types; assign per-node ids; bake { params, route } into Server Component elements.
  • fs-routes/types.ts / index.ts: new FsRouteObject and FsRouteComponentProps types; FsRouteModule.default documents the new prop shape.
  • fs-routes/tree.ts: export the existing isClientReference helper.
  • Docs: the File-System Routing page's stale-params note is replaced with a "Params on Client-Side Navigation" section covering the client/server split, the route prop, and the make-it-a-Client-Component escape hatch.

Tests

  • Unit (4 new): component-type decision for client references, baked params/route props on Server Component elements, id uniqueness, id portability across generated pages.
  • E2E fixture: recreates the issue's i18n repro ([lang] client layout, Server Component page with a useRouteParams client child, Client Component page via the re-export pattern).
  • E2E (4 new prod + 2 new dev): after /ja/en soft navigation, the client layout's params and the route-object read both show en while the Server Component page's baked output stays ja; direct loads render correct params; no page errors during navigation.

Full validation: unit tests, prod e2e suite (36/36), dev fs-routing e2e (8/8), typecheck, lint, and format all green.

🤖 Generated with Claude Code

https://claude.ai/code/session_01CMwvxdFVc7J44PszMubjsv


Generated by Claude Code

…e objects

Under file-system routing, all route components were baked into the RSC
payload as pre-rendered elements, so dynamic params were frozen at the
values of the initially-loaded page: soft client-side navigation between
pages of the same dynamic route (e.g. /ja -> /en under [lang]) kept
showing the old params everywhere, with no supported way to read the
live ones.

Two changes, per the discussion in #174:

- Pass a Client Component page/layout to FUNSTACK Router as a component
  type instead of a pre-rendered element. Client references serialize
  through the RSC boundary, so the router renders them in the browser
  with the params of the current match; their `params` prop now stays
  live across soft navigation (along with the router's other route
  component props).

- Assign a stable id to every generated route definition and pass
  Server Component pages/layouts a `route` prop: an opaque route object
  that can be forwarded to Client Components and given to the router's
  typed hooks (`useRouteParams(route)`) to read the live params of the
  URL currently shown. New types `FsRouteObject` and
  `FsRouteComponentProps` are exported from `@funstack/static/fs-routes`.

A Server Component page's own rendered output still reflects its
build-time params after soft navigation: static hosting serves one
pre-rendered payload per page and nothing can re-run a Server Component
in the browser. That remaining half of #174 needs on-navigation payload
fetching and stays open.

Refs #174

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CMwvxdFVc7J44PszMubjsv
Comment thread packages/docs/src/pages/learn/FileSystemRouting.mdx Outdated
Comment thread packages/docs/src/pages/learn/FileSystemRouting.mdx Outdated
Comment thread packages/static/src/fs-routes/tree.ts Outdated
…ects

Address review feedback on #176:

- Update @funstack/router to ^1.4.0 (dev/peer/fixture/docs deps). The
  router now passes component-typed routes its route definition as a
  `route` prop, so Client Component pages and layouts receive a route
  object from the router itself and the fs-routes `route` prop is
  uniform across Server and Client Components. The Client Component
  fixture page and e2e now cover useRouteParams(route) with the
  router-provided prop.

- Retype FsRouteObject on the router's new RouteHandle (the declared
  type of the `route` prop) instead of PartialRouteDefinition, and
  update FsRouteComponentProps docs for the uniform behavior.

- Move isClientReference into a shared util module
  (src/util/clientReference.ts).

- Docs: present the Server Component staleness as a temporary
  limitation being worked on (#174), and document that Client
  Components receive `route` from FUNSTACK Router v1.4+.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CMwvxdFVc7J44PszMubjsv
@uhyo
uhyo marked this pull request as ready for review August 27, 2026 14:43
@uhyo
uhyo merged commit 7fb713b into master Aug 27, 2026
2 checks passed
@uhyo
uhyo deleted the claude/issue-174-route-prop-1nr7v4 branch August 27, 2026 14:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants