Skip to content

[DRAFT] ⚗️ Add soft navigation tracking (experimental)#4154

Open
allspain wants to merge 4 commits intomainfrom
richard.klein/soft-navigation-support
Open

[DRAFT] ⚗️ Add soft navigation tracking (experimental)#4154
allspain wants to merge 4 commits intomainfrom
richard.klein/soft-navigation-support

Conversation

@allspain
Copy link
Collaborator

@allspain allspain commented Feb 10, 2026

Motivation

Single-page applications (SPAs) use client-side routing that doesn't trigger traditional page loads. The browser's Soft Navigation API (currently Chromium-only) detects these user-initiated navigations by observing URL changes paired with DOM modifications triggered by user interaction.

This PR adds experimental support for correlating the browser's soft navigation entries with RUM view events, enabling Datadog to distinguish user-initiated route changes from programmatic ones via a new view.is_soft_navigation boolean field.

Changes

All changes are gated behind the soft_navigation experimental feature flag — when disabled, the feature is a complete no-op with zero runtime cost.

Types & Schema (packages/rum-core/src/rawRumEvent.types.ts, packages/rum-core/src/browser/performanceObservable.ts)

  • Added optional is_soft_navigation boolean to RawRumViewEvent.view
  • Added SOFT_NAVIGATION performance entry type and RumSoftNavigationEntry interface

Configuration & Feature Detection (packages/core/src/tools/experimentalFeatures.ts, packages/core/src/domain/telemetry/telemetryEvent.types.ts, packages/rum-core/src/domain/configuration/)

  • Added ExperimentalFeature.SOFT_NAVIGATION enum value
  • Wired use_soft_navigation into configuration telemetry serialization

Soft Navigation Collection (packages/rum-core/src/domain/softNavigation/softNavigationCollection.ts)

  • Created startSoftNavigationCollection module that observes soft-navigation PerformanceEntry events via PerformanceObserver
  • Maintains a ValueHistory of soft navigation contexts for time-based lookups
  • Returns no-op contexts when the feature is disabled or the browser doesn't support the API

View Correlation (packages/rum-core/src/domain/view/viewCollection.ts, packages/rum-core/src/boot/startRum.ts)

  • Threaded softNavigationContexts into startViewCollection and processViewUpdate
  • For route_change views, uses findAll with a 50ms tolerance window to check if a soft navigation entry overlaps the view's start time, setting is_soft_navigation: true
  • The tolerance accounts for the timing gap between SDK view creation (triggered by history.pushState override) and Chrome finalizing the soft-navigation PerformanceEntry (~1ms on Vite, ~17ms on Turbopack/Next.js)

All changes in this PR were AI-agent generated.

SPA Integration Test Results

Tested with a monorepo of 4 SPA frameworks, each instrumented with the built SDK from this branch (enableExperimentalFeatures: ['soft_navigation']). Chrome chrome://flags/#soft-navigation-heuristics enabled.

SPA Framework Bundler Port is_soft_navigation Timing Gap
Vue 3 (Vue Router) Vite 4001 true ~1ms
Angular 19 (Angular Router) Angular CLI 4002 true ~2-3ms
SvelteKit (SvelteKit Router) Vite 4003 true ~1-2ms
Next.js 15 (App Router) Turbopack 4004 true ~17ms

Key findings:

  • Chrome's soft-navigation heuristic requires synchronous history.pushState() + DOM mutation in the same user-interaction task. Standard SPA router navigations update DOM asynchronously via their reactivity systems and do not produce soft-navigation PerformanceEntries — only direct pushState + DOM mutation in a click handler does.
  • The timing gap between SDK view creation and Chrome's soft-nav entry varies significantly by bundler: ~1ms on Vite vs ~17ms on Turbopack. The 50ms tolerance window accommodates all tested frameworks with headroom.
  • Initial fix used findSoftNavigationByTime (exact match) which failed due to this timing offset. Changed to findAll with a tolerance window to handle the gap.

Test instructions

Unit tests

yarn test:unit --spec packages/rum-core/src/domain/softNavigation/softNavigationCollection.spec.ts
yarn test:unit --spec packages/rum-core/src/domain/view/viewCollection.spec.ts

E2E tests (requires Chromium with soft-navigation flag)

yarn test:e2e -g "soft navigation"

The E2E tests cover three scenarios:

  1. User-initiated route change (click → DOM mutation → pushState) → is_soft_navigation: true
  2. Programmatic route change (pushState only, no user interaction) → is_soft_navigation is undefined
  3. Graceful degradation on browsers without soft navigation API support

Checklist

  • Tested locally
  • Tested against 4 SPA frameworks (Vue, Angular, Svelte, Next.js)
  • Tested on staging
  • Added unit tests for this change.
  • Added e2e/integration tests for this change.
  • Updated documentation and/or relevant AGENTS.md file

🤖 Generated with Claude Code

@allspain allspain requested a review from a team as a code owner February 10, 2026 08:41
@github-actions
Copy link


Thank you for your submission, we really appreciate it. Like many open-source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution. You can sign the CLA by just posting a Pull Request Comment same as the below format.


I have read the CLA Document and I hereby sign the CLA


You can retrigger this bot by commenting recheck in this Pull Request. Posted by the CLA Assistant Lite bot.

@allspain allspain changed the title ⚗️ Add soft navigation tracking (experimental) [DRAFT] ⚗️ Add soft navigation tracking (experimental) Feb 10, 2026
@allspain allspain force-pushed the richard.klein/soft-navigation-support branch from 5f97f80 to 8f02dac Compare February 10, 2026 08:45
@datadog-datadog-prod-us1
Copy link

datadog-datadog-prod-us1 bot commented Feb 10, 2026

✅ Tests

🎉 All green!

❄️ No new flaky tests detected
🧪 All tests passed

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: a8b399d | Docs | Datadog PR Page | Was this helpful? Give us feedback!

@allspain allspain force-pushed the richard.klein/soft-navigation-support branch from 8f02dac to 9f3daa1 Compare February 10, 2026 09:05
@cit-pr-commenter-54b7da
Copy link

cit-pr-commenter-54b7da bot commented Feb 10, 2026

Bundles Sizes Evolution

📦 Bundle Name Base Size Local Size 𝚫 𝚫% Status
Rum 168.98 KiB 169.99 KiB +1.01 KiB +0.60%
Rum Profiler 4.31 KiB 4.31 KiB +1 B +0.02%
Rum Recorder 24.54 KiB 24.54 KiB +1 B +0.00%
Logs 56.27 KiB 56.31 KiB +36 B +0.06%
Flagging 944 B 944 B 0 B 0.00%
Rum Slim 125.82 KiB 126.82 KiB +1019 B +0.79%
Worker 23.63 KiB 23.63 KiB 0 B 0.00%
🚀 CPU Performance

Pending...

🧠 Memory Performance

Pending...

🔗 RealWorld

allspain and others added 2 commits February 10, 2026 10:09
Add experimental soft navigation tracking behind a feature flag.
Listens for soft-navigation performance entries and correlates
them with RUM view events via is_soft_navigation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@allspain allspain force-pushed the richard.klein/soft-navigation-support branch from 9f3daa1 to 7f32214 Compare February 10, 2026 09:10
allspain and others added 2 commits February 10, 2026 14:15
… window

The soft-navigation PerformanceEntry's startTime is emitted ~1ms after the
view's startClocks.relative because the SDK detects the URL change (via
history.pushState override) slightly before Chrome finalizes the entry.
Using findSoftNavigationByTime (which requires entry.startTime <= queryTime)
always failed. Switch to findAll with a 5ms tolerance window to account for
this timing offset.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Next.js/Turbopack has a ~17ms gap between SDK view creation and Chrome's
soft-navigation PerformanceEntry, exceeding the original 5ms tolerance.
Tested and verified across all 4 SPAs (Vue, Angular, Svelte, Next.js).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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