Skip to content

🐛 Deduplicate Next.js views across discarded mount renders - #5111

Open
BeltranBulbarellaDD wants to merge 1 commit into
mainfrom
beltran.bulbarella/deduplicate-nextjs-render-views
Open

BeltranBulbarellaDD wants to merge 1 commit into
mainfrom
beltran.bulbarella/deduplicate-nextjs-render-views

Conversation

@BeltranBulbarellaDD

@BeltranBulbarellaDD BeltranBulbarellaDD commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

Motivation

Addresses #4931: a single page load can produce hundreds of RUM views with the same URL and distinct view IDs, without any navigation.

DatadogAppRouter starts the view during render and uses a component-local useRef to remember the pathname. If React abandons a mount render before it commits, a retry can receive a fresh ref. The guard resets, but the previous startView() call has already created a view.

For example, a client component rendered after DatadogAppRouter in the root layout can suspend while its JavaScript chunk loads. Without a Suspense boundary isolating that sibling, React can retry the root and start the same view on each attempt. The new E2E delays that chunk by 150 ms, reproducing the trigger described in this issue comment. Locally, the original implementation produced 253 distinct views for one load; the fix produces one.

Changes

Move the last started App Router pathname into the Next.js plugin's module state, so consecutive renders of the same pathname share the guard even when React discards the component instance. Only record the pathname after RUM initialization, and clear it when the plugin resets.

Deduplication uses the concrete pathname: /user/42 → /user/99 → /user/42 still creates three views even though all three have the normalized name /user/[id]. View creation stays in render, preserving its current timing and initial normalized name.

This fix covers consecutive retries of the same pathname. It does not prevent a view from being created for a different pathname whose render is later abandoned; that behavior was also reproduced with the original useRef implementation.

Test instructions

Manual reproduction using the Next.js test app:

  1. In test/apps/nextjs/app/lazy-hydration-marker.tsx, temporarily delay the lazy import:

    const HydrationMarker = lazy(async () => {
      await new Promise((resolve) => setTimeout(resolve, 150))
      return import('./hydration-marker')
    })

    This keeps the root layout suspended long enough for React to retry it. Keep the marker immediately after DatadogAppRouter, without adding a Suspense boundary around it.

  2. In test/apps/nextjs/instrumentation-client.js, replace the current code with:

const originalStartView = datadogRum.startView
datadogRum.startView = (...args) => {
  console.count('RUM startView')
  console.log('RUM view', args[0])
  return originalStartView(...args)
}

datadogRum.init({
  applicationId: '1234567890',
  clientToken: '1234567890',
  site: 'datadoghq.com',
  service: 'my-service',
  version: '1.0.0',
  env: 'production',
  plugins: [nextjsPlugin()],
})
  1. Build and start the production app from the repository root:

    yarn build
    yarn build:apps --app nextjs
    yarn --cwd test/apps/nextjs next start --port 3000
  2. Open DevTools, turn off Preserve log, and load http://localhost:3000/?delay-client-chunk. Wait for hydration to finish; performance.getEntriesByName('root-hydration-marker').length should be 1. With the fix, the console should show exactly one RUM startView call for /. Repeat a full reload to check again.

Checklist

  • Tested locally
  • Tested on staging
  • Added unit tests for this change.
  • Added e2e/integration tests for this change.
  • Updated documentation and/or relevant AGENTS.md file

@BeltranBulbarellaDD
BeltranBulbarellaDD requested a review from a team as a code owner October 6, 2026 10:16
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 6, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-06T10:18:43.766432Z b8c143f PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@datadog-prod-us1-3

datadog-prod-us1-3 Bot commented Oct 6, 2026 •

Copy link
Copy Markdown

Tests

✅ All CI checks and tests passed.

🎉 All green!

🧪 All tests passed
❄️ No new flaky tests detected

🎯 Code Coverage (details)
• Patch Coverage: 80.00%
• Overall Coverage: 77.17% (+0.00%)

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: b8c143f | Docs | View more details | Give us feedback!

@cit-pr-commenter-54b7da

Copy link
Copy Markdown

Bundles Sizes Evolution

📦 Bundle Name Base Size Local Size 𝚫 𝚫% Status
Rum Salesforce N/A 185.63 KiB N/A N/A N/A
Rum 185.61 KiB 185.61 KiB 0 B 0.00% ✅
Rum-salesforce Profiler N/A 8.38 KiB N/A N/A N/A
Rum Profiler 8.38 KiB 8.38 KiB 0 B 0.00% ✅
Rum-salesforce Recorder N/A 31.94 KiB N/A N/A N/A
Rum Recorder 31.94 KiB 31.94 KiB 0 B 0.00% ✅
Logs 59.53 KiB 59.53 KiB 0 B 0.00% ✅
Rum Slim 143.61 KiB 143.61 KiB 0 B 0.00% ✅
Worker 22.96 KiB 22.96 KiB 0 B 0.00% ✅
Rum Shopify N/A 216.02 KiB N/A N/A N/A
Rum-shopify Profiler N/A 8.38 KiB N/A N/A N/A
Rum-shopify Recorder N/A 4.77 KiB N/A N/A N/A

This branch has not been deployed

No deployments
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.

1 participant