Skip to content

TanStack: Support pathless layout routes (id-only) in story routing - #35465

Merged
huang-julien merged 8 commits into
storybookjs:nextfrom
unpunnyfuns:fix/tanstack-react-pathless-layout-routes
Jul 15, 2026
Merged

huang-julien merged 8 commits into
storybookjs:nextfrom
unpunnyfuns:fix/tanstack-react-pathless-layout-routes

Conversation

@unpunnyfuns

@unpunnyfuns unpunnyfuns commented Jul 11, 2026 •

Copy link
Copy Markdown
Contributor

Title: TanStack: Support pathless layout routes (id-only) in story routing

What I did

Pathless routes (_authed/ layouts, (group) directories) break story routing in
two places. First, the createFileRoute mock normalizes pathless ids to a URL
path: pure-pathless ids get path: '/', which never matches children (stories 404) and collides with sibling index routes, while nested ones
(/posts/_layout) duplicate their parent's path. Second, duplicateRouteTree
strips id from every clone, so a truly pathless route (id, no path) crashes
with Duplicate routes found with id: __root__.

Fix: ids whose final segment is pathless now produce id-only routes (matching
what the router generator emits), and duplicateRouteTree preserves explicit ids
on pathless routes (pathful routes keep the existing id-stripping).

Follow-through fixes so id-only routes work everywhere the framework touches
them: stories bound directly to a pathless layout route still render (the
decorator infers the mount URL by walking the cloned route chain's options,
since pre-init fullPath getters are undefined, and mounts through the layout's
existing index child, or a synthetic one); routeOverrides may give a pathless
layout a real path (id-preservation is decided after overrides); an explicit
options.path wins over a pathless id.

Reviewing in chunks

The five commits are ordered so each is independently green and reviewable:

  1. duplicateRouteTree id preservation (safe standalone; code-based id-only routes exist today)
  2. id-only pathless file routes in the createFileRoute mock (the core fix; depends on 1)
  3. decorator mount inference + index-child leaf for stories bound directly to pathless layouts (depends on 2)
  4. route-tree duplication matrix tests (droppable if a minimal diff is preferred)
  5. pathless-layout regression stories, in the pattern of the Outlet stories from [Bug]: @storybook/tanstack-react root layout not working #35007 (droppable)

Tests assert real router matching (router.state.matches after router.load())
across code-based and file-based trees, including createStoryRouter-level tests
for mount inference.

AI disclosure

Root-cause analysis, fix, and tests developed with Claude (Claude Code); reviewed and
submitted by me.

Manual testing

  • Unit/routing tests: cd code/frameworks/tanstack-react && yarn vitest run (50 tests in this package's suite; verified green at every commit).
  • Sandbox: the new PathlessLayout template stories render a leaf under a pathless layout at the layout-free URL (with beforeLoad executed) and a story bound directly to the pathless layout route.
  • End-to-end: published locally via verdaccio and consumed by a file-based TanStack Router SPA; all stories pass with the path: '/_authed/dashboard' workaround removed. Minimal repro available on request.

…icateRouteTree

cloneChild stripped id from every cloned route's options; a pathless route
(explicit id, no path) was cloned with neither identity and TanStack
registered it as a duplicate __root__. Preserve the explicit id when the
merged (post-override) options carry no path — an override that adds a path
re-derives identity from the path instead of tripping the id+path invariant.
A route whose final id segment is pathless (`_layout`, `(group)`) defines a
layout/group and contributes nothing to the URL, but createFileRoute
normalized such ids to path '/': a layout with path '/' never matches its
children (stories 404) and collides with a sibling index route
(Duplicate routes found with id: /). Emit id-only routes instead, matching
what @tanstack/router-generator produces. The segment-tail predicate also
covers pathless layouts nested under pathful segments (/posts/_layout),
which previously inherited their parent's path. An explicit options.path
wins over a pathless id.

BREAKING-ish: createFileRoute('/(group)') previously produced path '/'
(which never routed); it now produces an id-only route.
With pathless routes id-only, a story bound to the layout route itself could
no longer render: a pathless layout only matches when a child matches, and
the mount-path inference read init-backed getters (fullPath/id) that are
undefined on cloned, not-yet-init()ed routes. Resolve the story leaf to the
layout's index child — reusing an existing path:'/' child (attaching a
synthetic sibling would derive the same generated id and crash createRouter)
or attaching a synthetic bare one — and infer the mount URL by walking the
cloned parent chain's options (mountPathFor), which works before init and
skips pathless and bare '/' segments.
Routing-level coverage beyond the pathless fixes: nested pathful layouts
with index children, path params through the clone, group/layout/param
mixes shaped like routeTree.gen output, and routeOverrides addressed by
original id. Droppable if a minimal diff is preferred.
Sandbox regression stories in the pattern of the Outlet stories (storybookjs#35007):
a leaf under a pathless layout renders at the layout-free URL with
beforeLoad executed, and a story bound directly to the (nested) pathless
layout route mounts at the layout's position. Droppable if a minimal diff
is preferred.
@coderabbitai

coderabbitai Bot commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 7d460da5-379f-4692-a0d0-71f3d25d08ec

📥 Commits

Reviewing files that changed from the base of the PR and between 5e07a71 and 20b0894.

📒 Files selected for processing (8)
  • code/frameworks/tanstack-react/src/export-mocks/react-router.test.ts
  • code/frameworks/tanstack-react/src/export-mocks/react-router.ts
  • code/frameworks/tanstack-react/src/routing/decorator.test.ts
  • code/frameworks/tanstack-react/src/routing/decorator.tsx
  • code/frameworks/tanstack-react/src/routing/duplicate-tree.test.ts
  • code/frameworks/tanstack-react/src/routing/duplicate-tree.ts
  • code/frameworks/tanstack-react/src/routing/path-utils.ts
  • code/frameworks/tanstack-react/template/stories/PathlessLayout.stories.tsx

📝 Walkthrough

Walkthrough

TanStack React Router support now preserves pathless route identities, derives mount paths from ancestors, ensures pathless layouts have matchable index children, and adds normalization tests, cloning tests, router tests, and Storybook regression stories.

Changes

TanStack pathless route support

Layer / File(s) Summary
Pathless route normalization
code/frameworks/tanstack-react/src/routing/path-utils.ts, code/frameworks/tanstack-react/src/export-mocks/react-router.ts, code/frameworks/tanstack-react/src/export-mocks/react-router.test.ts
Pathless file-route IDs retain their IDs while path and full-path values are conditionally omitted or normalized, with coverage for root, group, layout, trailing-underscore, and explicit-path cases.
Route cloning and mount paths
code/frameworks/tanstack-react/src/routing/duplicate-tree.ts, code/frameworks/tanstack-react/src/routing/duplicate-tree.test.ts
Cloned pathless routes preserve identity, route overrides remain applicable, and mountPathFor derives normalized paths from ancestor route segments.
Story router matching
code/frameworks/tanstack-react/src/routing/decorator.tsx, code/frameworks/tanstack-react/src/routing/decorator.test.ts
Story router setup uses inferred mount paths and reuses or creates index children so pathless layout leaves can be matched.
Pathless layout regression stories
code/frameworks/tanstack-react/template/stories/PathlessLayout.stories.tsx
Storybook stories cover pathless layouts at the root, beneath pathful ancestors, and when bound directly to layout routes.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Storybook
  participant createStoryRouter
  participant duplicateRouteTree
  participant TanStackRouter
  Storybook->>createStoryRouter: resolve selected route and mount parameters
  createStoryRouter->>duplicateRouteTree: clone route tree with pathless identities
  duplicateRouteTree-->>createStoryRouter: return cloned route tree
  createStoryRouter->>TanStackRouter: mount at inferred ancestor path
  TanStackRouter-->>Storybook: match pathless layouts and leaf route
Loading

Possibly related issues


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@shilman
shilman requested a review from huang-julien July 11, 2026 07:46

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes Storybook’s TanStack Router integration to correctly handle pathless layout/group file routes (e.g. /_authed, /(group), /posts/_layout) by treating them as id-only routes and ensuring story routing/mounting works when such routes appear in the hierarchy or are bound directly in a story.

Changes:

  • Update the createFileRoute mock to emit id-only routes when the final segment is pathless (unless options.path is explicitly provided).
  • Adjust route-tree duplication to preserve explicit ids for pathless routes, while keeping existing id-stripping behavior for pathful routes.
  • Improve the decorator/router setup to infer mount URLs via ancestor paths and to make pathless layout leaves matchable by reusing/adding an index child; add regression tests and template stories.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
code/frameworks/tanstack-react/src/routing/path-utils.ts Adds isPathlessFileRouteId helper to detect ids whose final segment is pathless.
code/frameworks/tanstack-react/src/export-mocks/react-router.ts Makes createFileRoute emit id-only routes for pathless final segments; preserves explicit options.path.
code/frameworks/tanstack-react/src/export-mocks/react-router.test.ts Expands tests to cover id-only behavior for pathless ids and path/override precedence.
code/frameworks/tanstack-react/src/routing/duplicate-tree.ts Preserves ids for pathless clones; adds mountPathFor to compute mount URL from pathful ancestors.
code/frameworks/tanstack-react/src/routing/decorator.tsx Uses mountPathFor for path inference; ensures stories bound directly to pathless layouts still match by using/reusing an index child.
code/frameworks/tanstack-react/src/routing/duplicate-tree.test.ts Adds regression + matrix tests for duplication/matching with pathless layouts and overrides.
code/frameworks/tanstack-react/src/routing/decorator.test.ts Adds regression tests for mount path inference and index-child reuse with pathless layouts.
code/frameworks/tanstack-react/template/stories/PathlessLayout.stories.tsx Adds template regression stories validating rendering and beforeLoad under pathless layouts (including direct binding).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread code/frameworks/tanstack-react/src/routing/duplicate-tree.test.ts
Comment thread code/frameworks/tanstack-react/src/routing/duplicate-tree.test.ts
@huang-julien huang-julien added bug ci:normal Run our default set of CI jobs (choose this for most PRs). qa:success Pull Requests that were successfully QA'ed by the release team. labels Jul 13, 2026
unpunnyfuns added a commit to unpunnyfuns/storybook that referenced this pull request Jul 13, 2026
…t test

Review feedback on storybookjs#35465: keep plain Vitest unit tests free of the
Storybook test layer.
@unpunnyfuns

Copy link
Copy Markdown
Contributor Author

I've fixed the test imports in my next PR.

@huang-julien huang-julien added qa:skip Pull Requests that do not need any QA. (e.g. documentation) and removed qa:success Pull Requests that were successfully QA'ed by the release team. labels Jul 13, 2026
@huang-julien
huang-julien merged commit 3533366 into storybookjs:next Jul 15, 2026
146 checks passed
@huang-julien huang-julien added the patch:yes Bugfix & documentation PR that need to be picked to main branch label Jul 15, 2026
unpunnyfuns added a commit to unpunnyfuns/storybook that referenced this pull request Jul 15, 2026
…t test

Review feedback on storybookjs#35465: keep plain Vitest unit tests free of the
Storybook test layer.
@unpunnyfuns

Copy link
Copy Markdown
Contributor Author

I was not aware of #35197 by @krish-mittal1 and #35216 by @MPiland, both nearly a month before my PR.

My sincerest apologies. I will perform better due diligence in the future.

Ref: #35193, #35135

@MPiland

MPiland commented Jul 17, 2026

Copy link
Copy Markdown

I was not aware of #35197 by @krish-mittal1 and #35216 by @MPiland, both nearly a month before my PR.

My sincerest apologies. I will perform better due diligence in the future.

Ref: #35193, #35135

No need to apologize. I don't care if mine or someone else's fix gets merged. Just as long as it gets patched, I'm happy.

unpunnyfuns added a commit to unpunnyfuns/storybook that referenced this pull request Jul 17, 2026
…t test

Review feedback on storybookjs#35465: keep plain Vitest unit tests free of the
Storybook test layer.
@ndelangen ndelangen added patch:done Patch/release PRs already cherry-picked to main/release branch and removed patch:yes Bugfix & documentation PR that need to be picked to main branch labels Jul 17, 2026
unpunnyfuns added a commit to unpunnyfuns/storybook that referenced this pull request Jul 18, 2026
…t test

Review feedback on storybookjs#35465: keep plain Vitest unit tests free of the
Storybook test layer.
filipw01 pushed a commit to filipw01/storybook that referenced this pull request Jul 18, 2026
…act-pathless-layout-routes

TanStack: Support pathless layout routes (id-only) in story routing
(cherry picked from commit 3533366)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agent-scan:mixed bug ci:normal Run our default set of CI jobs (choose this for most PRs). patch:done Patch/release PRs already cherry-picked to main/release branch qa:skip Pull Requests that do not need any QA. (e.g. documentation) tanstack

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants