Skip to content

perf(link): skip reactivity when rendering on the server - #2774

Open
danielroe wants to merge 1 commit into
vuejs:mainfrom
danielroe:perf/ssr-reactivity
Open

perf(link): skip reactivity when rendering on the server#2774
danielroe wants to merge 1 commit into
vuejs:mainfrom
danielroe:perf/ssr-reactivity

Conversation

@danielroe

@danielroe danielroe commented Aug 9, 2026

Copy link
Copy Markdown
Member

Note

spotted when doing some benchmarking of <NuxtLink> that there was an opportunity to speed up rendering <RouterLink> for SSR.

on the server a <RouterLink> doesn't need to be reactive, but five computed values are produced by reactive(useLink(props)), which has a cost which we can avoid

to avoid two copies of most of the logic, this moves most computed values into plain functions which can be directly executed in the SSR fast-path, or built into computed values for client-side state.

I've done benchmarking locally using 200 links with renderToString (note that the mean value here is inflated by GC so the min value is truer unless using with lots of links)

min mean hz
main 0.563ms 0.910ms 1,100
this pr 0.384ms 0.416ms 2,404
plain <a> 0.147ms 0.176ms 5,679

my tentative headline is that renders <RouterLink> in server environments ~1.5-2.2x faster. per link that's still tiny: 4.55 μs -> 2.08 μs, but every little helps, right?

(you'll probably want to run your own numbers but I'd be very happy to share my benchmark script if that helps)

let me know if I've missed anything, or you think this is the wrong approach. we can also resolve this in nuxt separately but thought this would be useful to upstream.

Summary by CodeRabbit

  • Bug Fixes

    • Improved server-rendered links to match client-rendered behavior, including navigation, active states, CSS classes, and slot content.
    • Ensured links render consistently whether browser detection is enabled or disabled.
    • Improved support for nested, aliased, and parameterized routes during server-side rendering.
  • Tests

    • Added comprehensive coverage for server-rendered link scenarios and parity with reactive client rendering.

@netlify

netlify Bot commented Aug 9, 2026

Copy link
Copy Markdown

Deploy Preview for vue-router canceled.

Name Link
🔨 Latest commit 1b0c03e
🔍 Latest deploy log https://app.netlify.com/projects/vue-router/deploys/6a781f423ada5d000810875e

@coderabbitai

coderabbitai Bot commented Aug 9, 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 1c2e87e3-0448-411b-8cc6-0bb09c6fe3ae

📥 Commits

Reviewing files that changed from the base of the PR and between aab2876 and 1b0c03e.

📒 Files selected for processing (2)
  • packages/router/__tests__/RouterLink.ssr.spec.ts
  • packages/router/src/RouterLink.ts

📝 Walkthrough

Walkthrough

RouterLink now shares route and navigation helpers between client and SSR paths. SSR rendering resolves links synchronously without reactive setup. New tests cover link states, classes, slots, aliases, parameters, custom links, and browser-mode parity.

Changes

RouterLink SSR support

Layer / File(s) Summary
Shared RouterLink logic
packages/router/src/RouterLink.ts
Shared helpers now handle route validation, navigation, active-state resolution, and matched-record lookup. useLink uses these helpers.
Synchronous SSR rendering
packages/router/src/RouterLink.ts
The server path resolves route data and renders anchors or custom links without reactive setup.
SSR behavior validation
packages/router/__tests__/RouterLink.ssr.spec.ts
SSR tests cover link variants, classes, slots, aliases, parameters, custom links, browser-mode parity, and slot-prop parity.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant VueSSRRenderer
  participant RouterLink
  participant Router
  VueSSRRenderer->>RouterLink: Render RouterLink
  RouterLink->>Router: Resolve route and active state
  Router-->>RouterLink: Return href and navigation state
  RouterLink-->>VueSSRRenderer: Render anchor or custom link
Loading

Suggested reviewers: posva

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: skipping reactivity during server-side rendering of RouterLink.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@pkg-pr-new

pkg-pr-new Bot commented Aug 9, 2026

Copy link
Copy Markdown

Open in StackBlitz

pnpm add https://pkg.pr.new/vue-router@2774
npm i https://pkg.pr.new/vue-router@2774
yarn add https://pkg.pr.new/vue-router@2774.tgz

commit: 1b0c03e

@codecov

codecov Bot commented Aug 9, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 96.61017% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 87.08%. Comparing base (22f2595) to head (1b0c03e).
⚠️ Report is 2 commits behind head on main.

Files with missing lines Patch % Lines
packages/router/src/RouterLink.ts 96.61% 4 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2774      +/-   ##
==========================================
+ Coverage   86.99%   87.08%   +0.08%     
==========================================
  Files          93       93              
  Lines       10722    10785      +63     
  Branches     2478     2490      +12     
==========================================
+ Hits         9328     9392      +64     
  Misses       1388     1388              
+ Partials        6        5       -1     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@posva

posva commented Aug 11, 2026

Copy link
Copy Markdown
Member

If this applies to all computed, maybe we could directly apply the improvement to vue. What do you think?

We could also create an SSR RouterLink that only renders an <a>, my concern being the final client size increasing with this change

@danielroe

Copy link
Copy Markdown
Member Author

the speedup is largely from avoiding allocating the computed at all (e.g. no arrow function, no proxies), so I'm not sure we can achieve that in Vue itself.

@posva

posva commented Aug 18, 2026

Copy link
Copy Markdown
Member

I see. Let's hold on for the moment on this one: I'm not sure the added size and complexity here is worth it

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