Skip to content

fix(solid-router): avoid HeadContent remounts on history.replaceState#6998

Merged
Sheraff merged 7 commits intomainfrom
fix/solid-history-replacestate-fouc
Mar 21, 2026
Merged

fix(solid-router): avoid HeadContent remounts on history.replaceState#6998
Sheraff merged 7 commits intomainfrom
fix/solid-history-replacestate-fouc

Conversation

@Sheraff
Copy link
Contributor

@Sheraff Sheraff commented Mar 20, 2026

Summary

Screenshots

Before (warning flashing lights):

Screen.Recording.2026-03-20.at.20.50.02.mov

After:

Screen.Recording.2026-03-20.at.20.49.11.mov

Testing

  • CI=1 NX_DAEMON=false pnpm nx run @tanstack/solid-router:test:unit --outputStyle=stream --skipRemoteCache
  • CI=1 NX_DAEMON=false pnpm nx run @tanstack/solid-router:test:types --outputStyle=stream --skipRemoteCache
  • CI=1 NX_DAEMON=false pnpm nx run @tanstack/solid-router:test:eslint --outputStyle=stream --skipRemoteCache

Summary by CodeRabbit

  • Bug Fixes

    • Prevented duplicate stylesheet links from being introduced during navigation and history/state updates.
  • Performance

    • Improved head tag reconciliation by memoizing tag computation and using structural-equality checks to avoid unnecessary DOM updates.
  • Tests

    • Added browser-style tests across Solid/React/Vue packages to assert manifest stylesheet links remain the same DOM element and are deduplicated during history/state changes and navigation.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 20, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Memoizes Solid's useTags via createMemo and applies replaceEqualDeep(prev, next) to preserve structural equality for head tag arrays; adds browser-oriented tests in Solid, React, and Vue packages that verify manifest stylesheet <link href="/main.css"> element identity and deduplication across navigation and history state changes.

Changes

Cohort / File(s) Summary
Head Content Optimization
packages/solid-router/src/headContentUtils.tsx
useTags now returns a Solid createMemo accessor; imports replaceEqualDeep and uses replaceEqualDeep(prev, next) when prev exists to preserve structural equality and avoid redundant tag updates; existing deduplication logic retained.
Solid Router Tests
packages/solid-router/tests/Scripts.test.tsx
Expanded testing imports and added global afterEach (history reset + cleanup()); added createTestManifest helper and browser-history tests that mount RouterProvider with HeadContent to assert /main.css link element stability, no removals, and single-instance deduplication across <Link> navigation and history.replaceState.
React Router Tests
packages/react-router/tests/Scripts.test.tsx
Added testing utilities, createPortal, router helpers, and global afterEach; new client-side tests portal HeadContent into document.head with an SSR manifest stylesheet and assert the /main.css <link> remains the same DOM element and unique after history state changes and <Link> navigation.
Vue Router Tests
packages/vue-router/tests/Scripts.test.tsx
Added testing utilities, Teleport, router primitives, and global afterEach; new client-navigation SSR tests with createTestManifest ensure the manifest /main.css <link> element identity and single-instance presence after history.replaceState and <Link> navigation.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐇 I hopped through tags with memos bright,

Kept one stylesheet snug and tight.
No duplicates chased across the way,
Tests nod as links persist and stay.
Small hops, tidy DOM — I cheer today.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main fix: preventing HeadContent remounts on history.replaceState in solid-router, which directly aligns with the core change in useTags and the primary objective.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/solid-history-replacestate-fouc

Comment @coderabbitai help to get the list of available commands and usage tips.

@nx-cloud
Copy link

nx-cloud bot commented Mar 20, 2026

View your CI Pipeline Execution ↗ for commit 0e262ff

Command Status Duration Result
nx affected --targets=test:eslint,test:unit,tes... ✅ Succeeded 2m 11s View ↗
nx run-many --target=build --exclude=examples/*... ✅ Succeeded 3s View ↗

☁️ Nx Cloud last updated this comment at 2026-03-20 21:18:30 UTC

@github-actions
Copy link
Contributor

github-actions bot commented Mar 20, 2026

🚀 Changeset Version Preview

No changeset entries found. Merging this PR will not cause a version bump for any packages.

@github-actions
Copy link
Contributor

github-actions bot commented Mar 20, 2026

Bundle Size Benchmarks

  • Commit: 21e39bde0832
  • Measured at: 2026-03-20T21:16:56.795Z
  • Baseline source: history:91cc62899b75
  • Dashboard: bundle-size history
Scenario Current (gzip) Delta vs baseline Raw Brotli Trend
react-router.minimal 88.56 KiB 0 B (0.00%) 279.34 KiB 76.84 KiB ▁▁▁▁▁▁▁▁███
react-router.full 91.73 KiB 0 B (0.00%) 290.07 KiB 79.48 KiB ▁▁▁▁▁▁▁▁███
solid-router.minimal 36.13 KiB 0 B (0.00%) 108.98 KiB 32.41 KiB ████████▁▁▁
solid-router.full 40.49 KiB +15 B (+0.04%) 122.12 KiB 36.25 KiB ████████▁▁▁▁
vue-router.minimal 54.11 KiB 0 B (0.00%) 155.27 KiB 48.48 KiB ▁▁▁▁▁▁▁▁███
vue-router.full 58.89 KiB 0 B (0.00%) 170.42 KiB 52.63 KiB ▁▁▁▁▁▁▁▁███
react-start.minimal 103.00 KiB 0 B (0.00%) 327.41 KiB 89.02 KiB ▁▁▁▁▁▁▁▁███
react-start.full 106.37 KiB 0 B (0.00%) 337.72 KiB 91.83 KiB ▁▁▁▁▁▁▁▁███
solid-start.minimal 50.32 KiB 0 B (0.00%) 155.47 KiB 44.32 KiB ████████▁▁▁
solid-start.full 55.73 KiB +12 B (+0.02%) 171.31 KiB 48.94 KiB ████████▁▁▁▁

Trend sparkline is historical gzip bytes ending with this PR measurement; lower is better.

@pkg-pr-new
Copy link

pkg-pr-new bot commented Mar 20, 2026

More templates

@tanstack/arktype-adapter

npm i https://pkg.pr.new/@tanstack/arktype-adapter@6998

@tanstack/eslint-plugin-router

npm i https://pkg.pr.new/@tanstack/eslint-plugin-router@6998

@tanstack/history

npm i https://pkg.pr.new/@tanstack/history@6998

@tanstack/nitro-v2-vite-plugin

npm i https://pkg.pr.new/@tanstack/nitro-v2-vite-plugin@6998

@tanstack/react-router

npm i https://pkg.pr.new/@tanstack/react-router@6998

@tanstack/react-router-devtools

npm i https://pkg.pr.new/@tanstack/react-router-devtools@6998

@tanstack/react-router-ssr-query

npm i https://pkg.pr.new/@tanstack/react-router-ssr-query@6998

@tanstack/react-start

npm i https://pkg.pr.new/@tanstack/react-start@6998

@tanstack/react-start-client

npm i https://pkg.pr.new/@tanstack/react-start-client@6998

@tanstack/react-start-server

npm i https://pkg.pr.new/@tanstack/react-start-server@6998

@tanstack/router-cli

npm i https://pkg.pr.new/@tanstack/router-cli@6998

@tanstack/router-core

npm i https://pkg.pr.new/@tanstack/router-core@6998

@tanstack/router-devtools

npm i https://pkg.pr.new/@tanstack/router-devtools@6998

@tanstack/router-devtools-core

npm i https://pkg.pr.new/@tanstack/router-devtools-core@6998

@tanstack/router-generator

npm i https://pkg.pr.new/@tanstack/router-generator@6998

@tanstack/router-plugin

npm i https://pkg.pr.new/@tanstack/router-plugin@6998

@tanstack/router-ssr-query-core

npm i https://pkg.pr.new/@tanstack/router-ssr-query-core@6998

@tanstack/router-utils

npm i https://pkg.pr.new/@tanstack/router-utils@6998

@tanstack/router-vite-plugin

npm i https://pkg.pr.new/@tanstack/router-vite-plugin@6998

@tanstack/solid-router

npm i https://pkg.pr.new/@tanstack/solid-router@6998

@tanstack/solid-router-devtools

npm i https://pkg.pr.new/@tanstack/solid-router-devtools@6998

@tanstack/solid-router-ssr-query

npm i https://pkg.pr.new/@tanstack/solid-router-ssr-query@6998

@tanstack/solid-start

npm i https://pkg.pr.new/@tanstack/solid-start@6998

@tanstack/solid-start-client

npm i https://pkg.pr.new/@tanstack/solid-start-client@6998

@tanstack/solid-start-server

npm i https://pkg.pr.new/@tanstack/solid-start-server@6998

@tanstack/start-client-core

npm i https://pkg.pr.new/@tanstack/start-client-core@6998

@tanstack/start-fn-stubs

npm i https://pkg.pr.new/@tanstack/start-fn-stubs@6998

@tanstack/start-plugin-core

npm i https://pkg.pr.new/@tanstack/start-plugin-core@6998

@tanstack/start-server-core

npm i https://pkg.pr.new/@tanstack/start-server-core@6998

@tanstack/start-static-server-functions

npm i https://pkg.pr.new/@tanstack/start-static-server-functions@6998

@tanstack/start-storage-context

npm i https://pkg.pr.new/@tanstack/start-storage-context@6998

@tanstack/valibot-adapter

npm i https://pkg.pr.new/@tanstack/valibot-adapter@6998

@tanstack/virtual-file-routes

npm i https://pkg.pr.new/@tanstack/virtual-file-routes@6998

@tanstack/vue-router

npm i https://pkg.pr.new/@tanstack/vue-router@6998

@tanstack/vue-router-devtools

npm i https://pkg.pr.new/@tanstack/vue-router-devtools@6998

@tanstack/vue-router-ssr-query

npm i https://pkg.pr.new/@tanstack/vue-router-ssr-query@6998

@tanstack/vue-start

npm i https://pkg.pr.new/@tanstack/vue-start@6998

@tanstack/vue-start-client

npm i https://pkg.pr.new/@tanstack/vue-start-client@6998

@tanstack/vue-start-server

npm i https://pkg.pr.new/@tanstack/vue-start-server@6998

@tanstack/zod-adapter

npm i https://pkg.pr.new/@tanstack/zod-adapter@6998

commit: 0e262ff

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/solid-router/tests/Scripts.test.tsx`:
- Around line 248-264: Replace the unsafe cast "as any" on the router.ssr
assignment with a concrete Manifest type: import Manifest from
"@tanstack/router-core" (or use the satisfies operator) and change the
assignment so it satisfies { ssr: { manifest: Manifest } } for the object
constructed for router.ssr (the block that builds manifest.routes for
[rootRoute.id] with assets). This preserves type safety for the manifest used by
the test while keeping the same structure for router.ssr and the entry keyed by
rootRoute.id.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 363bcdfb-b88c-4247-875c-8325b80f815a

📥 Commits

Reviewing files that changed from the base of the PR and between 21e39bd and 7b8f7e9.

📒 Files selected for processing (2)
  • packages/solid-router/src/headContentUtils.tsx
  • packages/solid-router/tests/Scripts.test.tsx

@codspeed-hq
Copy link

codspeed-hq bot commented Mar 20, 2026

Merging this PR will not alter performance

✅ 6 untouched benchmarks


Comparing fix/solid-history-replacestate-fouc (0e262ff) with main (91cc628)1

Open in CodSpeed

Footnotes

  1. No successful run was found on main (21e39bd) during the generation of this report, so 91cc628 was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
packages/vue-router/tests/Scripts.test.tsx (1)

256-272: Keep the SSR manifest fixture typed here too.

This repeats the same type escape pattern as the React coverage: as any on router.ssr plus a cast on location.state. That makes the regression less trustworthy because the synthetic fixture can drift away from the real SSR/state contract without TS catching it.

♻️ Example of the same typed-fixture pattern
-      router.ssr = {
-        manifest: {
-          routes: {
-            [rootRoute.id]: {
-              assets: [
-                {
-                  tag: 'link',
-                  attrs: {
-                    rel: 'stylesheet',
-                    href: '/main.css',
-                  },
-                },
-              ],
-            },
-          },
-        },
-      } as any
+      const manifest = {
+        routes: {
+          [rootRoute.id]: {
+            assets: [
+              {
+                tag: 'link',
+                attrs: {
+                  rel: 'stylesheet',
+                  href: '/main.css',
+                },
+              },
+            ],
+          },
+        },
+      } satisfies NonNullable<NonNullable<typeof router.ssr>['manifest']>
+
+      router.ssr = { ...(router.ssr ?? {}), manifest }

       await waitFor(() => {
-        expect(
-          (router.state.location.state as { slideId?: string }).slideId,
-        ).toBe('slide-2')
+        expect(router.state.location.state).toMatchObject({
+          slideId: 'slide-2',
+        })
       })

As per coding guidelines, **/*.{ts,tsx}: Use TypeScript strict mode with extensive type safety.

Also applies to: 294-297, 345-361

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/vue-router/tests/Scripts.test.tsx` around lines 256 - 272, The test
fixture currently uses an unsafe `as any` on `router.ssr` (and similar on
`location.state`), which defeats TypeScript checks; replace the `as any` casts
by constructing the fixture with the proper SSR/state types (e.g., import or
reference the library's SSR manifest/interface and cast using that exact type)
so `router.ssr = { manifest: { routes: { [rootRoute.id]: { assets: [...] } } }
}` is typed as the real SSR manifest type; do the same for the `location.state`
fixture so both `router.ssr`, `manifest`, `routes`, `rootRoute.id`, and
`location.state` conform to the actual interfaces instead of using `any`.
packages/react-router/tests/Scripts.test.tsx (1)

383-399: Avoid type escapes in the new regression fixture.

These tests bypass the exact contract they are supposed to lock down with as any and an ad-hoc cast on location.state, so manifest/state drift can compile cleanly and quietly weaken the regression. Please keep the synthetic manifest and state assertion typed.

♻️ Example of keeping the fixture typed
-      router.ssr = {
-        manifest: {
-          routes: {
-            [rootRoute.id]: {
-              assets: [
-                {
-                  tag: 'link',
-                  attrs: {
-                    rel: 'stylesheet',
-                    href: '/main.css',
-                  },
-                },
-              ],
-            },
-          },
-        },
-      } as any
+      const manifest = {
+        routes: {
+          [rootRoute.id]: {
+            assets: [
+              {
+                tag: 'link',
+                attrs: {
+                  rel: 'stylesheet',
+                  href: '/main.css',
+                },
+              },
+            ],
+          },
+        },
+      } satisfies NonNullable<NonNullable<typeof router.ssr>['manifest']>
+
+      router.ssr = { ...(router.ssr ?? {}), manifest }

       await waitFor(() => {
-        expect(
-          (router.state.location.state as { slideId?: string }).slideId,
-        ).toBe('slide-2')
+        expect(router.state.location.state).toMatchObject({
+          slideId: 'slide-2',
+        })
       })

As per coding guidelines, **/*.{ts,tsx}: Use TypeScript strict mode with extensive type safety.

Also applies to: 419-422, 468-484

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages/vue-router/tests/Scripts.test.tsx`:
- Around line 256-272: The test fixture currently uses an unsafe `as any` on
`router.ssr` (and similar on `location.state`), which defeats TypeScript checks;
replace the `as any` casts by constructing the fixture with the proper SSR/state
types (e.g., import or reference the library's SSR manifest/interface and cast
using that exact type) so `router.ssr = { manifest: { routes: { [rootRoute.id]:
{ assets: [...] } } } }` is typed as the real SSR manifest type; do the same for
the `location.state` fixture so both `router.ssr`, `manifest`, `routes`,
`rootRoute.id`, and `location.state` conform to the actual interfaces instead of
using `any`.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 590e07e6-6579-469e-adcb-236f042a861c

📥 Commits

Reviewing files that changed from the base of the PR and between f12ac8d and 2ec0e55.

📒 Files selected for processing (2)
  • packages/react-router/tests/Scripts.test.tsx
  • packages/vue-router/tests/Scripts.test.tsx

@Sheraff Sheraff requested a review from birkskyum March 20, 2026 19:59
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (3)
packages/vue-router/tests/Scripts.test.tsx (1)

282-309: Optional: extract shared stylesheet assertions into a local helper.

The getStylesheetLink lookup and uniqueness assertion are repeated; a tiny helper would reduce duplication and keep test intent even clearer.

Also applies to: 357-382

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/vue-router/tests/Scripts.test.tsx` around lines 282 - 309, Extract
the repeated DOM queries into a small local helper and reuse it in both places
(lines around the existing getStylesheetLink usage and the similar block at
357-382); specifically keep the existing getStylesheetLink logic
(querySelectorAll('link[rel="stylesheet"]') and href === '/main.css') or rename
it to a clearer helper like getMainStylesheetLink(), and add a companion
assertion helper (e.g., assertSingleMainStylesheet()) that asserts the returned
element is an HTMLLinkElement and that only one link with href '/main.css'
exists; replace the duplicated inline queries and uniqueness checks with calls
to these helpers in the test blocks that currently reference getStylesheetLink
and the Array.from(...).filter(...) uniqueness assertion.
packages/solid-router/tests/Scripts.test.tsx (1)

179-212: Optional: extract shared head-link query/cleanup helpers.

Both tests repeat the same stylesheet lookup and removal logic; local helper extraction would reduce repetition and keep intent focused.

Also applies to: 351-386

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/solid-router/tests/Scripts.test.tsx` around lines 179 - 212, Extract
the repeated stylesheet lookup and cleanup logic into shared helper functions
(e.g., getStylesheetLink() and removeStylesheetLinks(href: string)) and replace
the duplicated blocks in the test (the getStylesheetLink definition, the final
forEach cleanup, and the similar code around lines 351-386) with calls to those
helpers; keep the helpers scoped to the test file (top of the describe or test
suite) so tests call getStylesheetLink() to assert presence and
removeStylesheetLinks('/main.css') in the finally cleanup.
packages/react-router/tests/Scripts.test.tsx (1)

409-434: Optional: de-duplicate repeated stylesheet lookup/assertion blocks.

A shared helper in this describe block would trim repetition and make future maintenance easier without changing behavior.

Also applies to: 480-503

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/react-router/tests/Scripts.test.tsx` around lines 409 - 434, Create
a shared helper function in the test's describe block to centralize the repeated
stylesheet lookup/assertions: move the inline getStylesheetLink logic into a
single helper (e.g., getStylesheetLink()) used by the tests that currently
duplicate it (the block around the click/assert and the other similar block at
lines 480-503). Update tests that call the inline
Array.from(document.querySelectorAll('link[rel="stylesheet"]'))/... checks to
call the helper and reuse a single assertion helper for
"expect(...).toBeInstanceOf(HTMLLinkElement)" and the length check, so both the
initialLink capture, post-click identity check, and the duplicate-count
assertion use the centralized helper instead of repeating the DOM query.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages/react-router/tests/Scripts.test.tsx`:
- Around line 409-434: Create a shared helper function in the test's describe
block to centralize the repeated stylesheet lookup/assertions: move the inline
getStylesheetLink logic into a single helper (e.g., getStylesheetLink()) used by
the tests that currently duplicate it (the block around the click/assert and the
other similar block at lines 480-503). Update tests that call the inline
Array.from(document.querySelectorAll('link[rel="stylesheet"]'))/... checks to
call the helper and reuse a single assertion helper for
"expect(...).toBeInstanceOf(HTMLLinkElement)" and the length check, so both the
initialLink capture, post-click identity check, and the duplicate-count
assertion use the centralized helper instead of repeating the DOM query.

In `@packages/solid-router/tests/Scripts.test.tsx`:
- Around line 179-212: Extract the repeated stylesheet lookup and cleanup logic
into shared helper functions (e.g., getStylesheetLink() and
removeStylesheetLinks(href: string)) and replace the duplicated blocks in the
test (the getStylesheetLink definition, the final forEach cleanup, and the
similar code around lines 351-386) with calls to those helpers; keep the helpers
scoped to the test file (top of the describe or test suite) so tests call
getStylesheetLink() to assert presence and removeStylesheetLinks('/main.css') in
the finally cleanup.

In `@packages/vue-router/tests/Scripts.test.tsx`:
- Around line 282-309: Extract the repeated DOM queries into a small local
helper and reuse it in both places (lines around the existing getStylesheetLink
usage and the similar block at 357-382); specifically keep the existing
getStylesheetLink logic (querySelectorAll('link[rel="stylesheet"]') and href ===
'/main.css') or rename it to a clearer helper like getMainStylesheetLink(), and
add a companion assertion helper (e.g., assertSingleMainStylesheet()) that
asserts the returned element is an HTMLLinkElement and that only one link with
href '/main.css' exists; replace the duplicated inline queries and uniqueness
checks with calls to these helpers in the test blocks that currently reference
getStylesheetLink and the Array.from(...).filter(...) uniqueness assertion.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: aca8d516-7ea3-45be-8202-5bc7186ba548

📥 Commits

Reviewing files that changed from the base of the PR and between 2ec0e55 and 370eb61.

📒 Files selected for processing (3)
  • packages/react-router/tests/Scripts.test.tsx
  • packages/solid-router/tests/Scripts.test.tsx
  • packages/vue-router/tests/Scripts.test.tsx

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
packages/solid-router/tests/Scripts.test.tsx (1)

232-241: Extract repeated stylesheet cleanup into a small helper.

The /main.css cleanup logic is duplicated across both finally blocks. A shared local helper would reduce repetition and make future test updates safer.

Refactor sketch
+const removeMainCssLinks = () => {
+  document.head.querySelectorAll('link[rel="stylesheet"]').forEach((link) => {
+    if (link.getAttribute('href') === '/main.css') {
+      link.remove()
+    }
+  })
+}
...
-      document.head
-        .querySelectorAll('link[rel="stylesheet"]')
-        .forEach((link) => {
-          if (link.getAttribute('href') === '/main.css') {
-            link.remove()
-          }
-        })
+      removeMainCssLinks()
...
-      document.head
-        .querySelectorAll('link[rel="stylesheet"]')
-        .forEach((link) => {
-          if (link.getAttribute('href') === '/main.css') {
-            link.remove()
-          }
-        })
+      removeMainCssLinks()

Also applies to: 407-415

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/solid-router/tests/Scripts.test.tsx` around lines 232 - 241, Extract
the duplicated "/main.css" removal logic into a small local helper (e.g.,
removeMainCssLink) inside the test file and call it from both finally blocks
instead of repeating the querySelectorAll/forEach block; locate the duplicated
code around the finally blocks that reference observer and history and replace
each repeated snippet with a call to the new helper so both cleanup sites share
the same implementation.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages/solid-router/tests/Scripts.test.tsx`:
- Around line 232-241: Extract the duplicated "/main.css" removal logic into a
small local helper (e.g., removeMainCssLink) inside the test file and call it
from both finally blocks instead of repeating the querySelectorAll/forEach
block; locate the duplicated code around the finally blocks that reference
observer and history and replace each repeated snippet with a call to the new
helper so both cleanup sites share the same implementation.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 06f04c35-55d2-4b75-a682-5c0e8be5fea3

📥 Commits

Reviewing files that changed from the base of the PR and between 370eb61 and 2b51687.

📒 Files selected for processing (1)
  • packages/solid-router/tests/Scripts.test.tsx

@Sheraff Sheraff merged commit 0d11d5e into main Mar 21, 2026
17 checks passed
@Sheraff Sheraff deleted the fix/solid-history-replacestate-fouc branch March 21, 2026 06:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants