Skip to content

useParams triggers phantom renders on navigation #13487

@amadeus

Description

@amadeus

I'm using React Router as a...

framework

Reproduction

I've created a repo that shows the bug with a very detailed instructions on what's happening and how to reproduce this issue:

https://github.com/amadeus/rr-params-issue

One should be able to easily clone the repo, install deps, run the dev server and then follow along with the console logs and README to get exact details on the issue.

System Info

System:
    OS: macOS 15.4.1
    CPU: (16) arm64 Apple M4 Max
    Memory: 60.70 GB / 128.00 GB
    Shell: 4.0.0 - /usr/local/bin/fish
  Binaries:
    Node: 22.14.0 - ~/.proto/shims/node
    npm: 10.9.2 - ~/.local/state/fnm_multishells/89190_1745882005446/bin/npm
    pnpm: 10.4.0 - ~/.proto/shims/pnpm
    bun: 1.2.2 - ~/.proto/shims/bun
  Browsers:
    Chrome: 135.0.7049.115
    Safari: 18.4
  npmPackages:
    @react-router/dev: ^7.5.3 => 7.5.3
    @react-router/node: ^7.5.3 => 7.5.3
    @react-router/serve: ^7.5.3 => 7.5.3
    react-router: ^7.5.3 => 7.5.3
    vite: ^5.4.18 => 5.4.18

Used Package Manager

npm

Expected Behavior

I believe the expectation is that when using a hook like useParams, it should only broadcast context updates when params actually change. I recognize that things like react compiler ultimately help downplay the effects of these updates, but I'd still argue given the surface API of hook, it should only trigger a re-render when params actually change. It's potentially a silent footgun that can be hard to reason about and plan for.

Actual Behavior

The useParams hook appears to trigger extra renders around navigation events with new params that aren't actually new and just the same params from prior to the navigation (I.E. the returned objects from useParams aren't even stable despite being shallowly equal). Then they emit after the url actually changes with the new expected params.

This results in ghost/phantom renders right before navigation events that is often already an expensive sequence of events given page navigation events.

Ultimately I think there are two core issues here:

  1. useParams should only fire when params actually change
  2. useParams should always return a stable object for params when they are actually shallowly equal (not really an issue if they only fire appropriately, at least from what I could tell in practice)

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions