Link component leads to unnecessary re-renders with legacyBehavior
Nobody has claimed this yet.
- Dominant language
- JavaScript
- Stars
- 142k
- Forks
- 32.4k
- Avg merge
- 2d 14h
- Merged PRs (30d)
- 351
Description
Link to the code that reproduces this issue
https://codesandbox.io/p/devbox/hardcore-neumann-dw8484
To Reproduce
Click the "Update Time" button, which updates a query parameter in the URL to the current time. Observe that the component "MyButton" re-renders due to the "Link" wrapper after a query param changed, despite its render being totally independent to changing query parameters and using React.memo
Current vs. Expected behavior
Current behavior is that the Link component uses useRouter() causing it to re-render when any query parameters change. This causes it to pass a new instance of the 'onClick', 'onMouseEnter', and 'onTouchStart' props to the child, which forces it to rerender.
Expected behavior is that the Link component should not need to re-render at all in this case, and even if it did re-render it should pass a stable function for onClick/onMouseEnter/onTouchStart to prevent the button from re-rendering (since only query params have changed, nothing related to the button's navigation has changed).
This causes many unnecessary re-renders across a website when navigations happen, in particular for components which remain mounted such as a header/left-rail or when only query parameters are changing (which doesn't impact most of the page typically).
Provide environment information
Operating System:
Platform: linux
Arch: x64
Version: #1 SMP PREEMPT_DYNAMIC Sun Aug 6 20:05:33 UTC 2023
Available memory (MB): 4102
Available CPU cores: 2
Binaries:
Node: 20.9.0
npm: 9.8.1
Yarn: 1.22.19
pnpm: 8.10.2
Relevant Packages:
next: 15.0.0-canary.104 // Latest available version is detected (15.0.0-canary.104).
eslint-config-next: N/A
react: 19.0.0-rc-06d0b89e-20240801
react-dom: 19.0.0-rc-06d0b89e-20240801
typescript: 5.3.3
Next.js Config:
output: N/A
Which area(s) are affected? (Select all that apply)
Navigation, Performance, Runtime
Which stage(s) are affected? (Select all that apply)
next dev (local), next build (local), next start (local), Vercel (Deployed), Other (Deployed)
Additional context
I started a related discussion at https://github.com/vercel/next.js/discussions/67520, but there was no traction there.
Also note that legacyBehavior is required for any custom button components (ex. using MUI or styled-components) and recommended in docs, so I don't think its valid to ignore
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the linked CodeSandbox and reproduce the update caused by changing the query parameter, then inspect the Link component's legacyBehavior path. Compare MyButton's renders and the onClick, onMouseEnter, and onTouchStart props before and after navigation. Done means query-parameter changes no longer cause this independent memoized child to re-render unnecessarily.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript, nextjs, react
- Domain
- frontend, performance, web-dev
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100