Skip to content

Conversation

@dsdeur
Copy link

@dsdeur dsdeur commented Oct 23, 2025

Problem

When using the React Link component in an SVG it causes a full refresh, unless the target prop is explicitly set.

Cause

This is caused by retrieving the element target with .target:

const elementTarget = (e.currentTarget as HTMLAnchorElement).target

In an SVG, element.target returns an SVGAnimatedString instead of a string, making the target check evaluate to false:

(!effectiveTarget || effectiveTarget === '_self') &&

Resulting in the navigate function to not be triggered and the default link behavior to cause a full refresh.

Example/demo

https://stackblitz.com/edit/github-9fsuvtph?file=src%2Froutes%2Findex.tsx

  • The red rectangle causes a full refresh as the target check fails
  • The blue rectangle works as it should, as the target prop is set, which overrules the element target

Solution

The easiest solution seems to be to use .getAttribute("target") instead of .target as that returns null or the value instead of the SVGAnimatedString.

(This is the same approach as Next.js link uses)

Summary by CodeRabbit

  • New Features

    • Added accessible, clickable SVG links in app examples that navigate to the Posts page.
  • Bug Fixes

    • Improved link click handling so SVG-based links respect link targets and no longer cause full page reloads.
  • Tests

    • Added end-to-end tests verifying SVG links navigate to /posts without triggering a full page reload.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 23, 2025

Walkthrough

Link click handling now reads the element's target via getAttribute('target') (supports HTMLAnchorElement | SVGAElement) to compute interception; Solid/React link render paths were adjusted; small inline SVG Links were added to React and Solid e2e apps with tests asserting SVG clicks don't trigger full page reloads.

Changes

Cohort / File(s) Summary
React router link
packages/react-router/src/link.tsx
Read link target via element.getAttribute('target') instead of e.currentTarget.target; adjust effectiveTarget computation for `HTMLAnchorElement
Solid router link
packages/solid-router/src/link.tsx
Read target via getAttribute('target'); refactored rendering to use Dynamic in createLink and simplified Link to render a plain anchor (removed _asChild runtime path).
E2E React app UI
e2e/react-router/basic/src/main.tsx
Added an inline SVG wrapped in a Link (20×20 blue rounded rect, title and aria-label) pointing to /posts.
E2E React tests
e2e/react-router/basic/tests/app.spec.ts
Added test setup (PORT) and an E2E test "Link in SVG does not trigger a full page reload" that clicks the SVG link and asserts no full page reload via DOMContentLoaded monitoring.
E2E Solid app UI
e2e/solid-router/basic/src/main.tsx
Added an inline SVG link (using useLinkProps) pointing to /posts, with aria-label.
E2E Solid tests
e2e/solid-router/basic/tests/app.spec.ts
Added test setup (PORT) and an E2E test "Link in SVG does not trigger a full page reload" that clicks the SVG link and asserts no full page reload via DOMContentLoaded monitoring.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Element as Link element (a or svg)
    participant ClickHandler as Link.onClick
    participant Router as Router/navigation

    User->>Element: click
    Element->>ClickHandler: event dispatched
    ClickHandler->>Element: element.getAttribute("target")
    alt target empty / same-origin / not _blank
        ClickHandler->>Router: intercept -> client-side navigate
        Router-->>User: SPA navigation (no full reload)
    else target="_blank" or external
        ClickHandler-->>User: allow default (open new tab/navigation)
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Areas to inspect closely:
    • getAttribute vs property semantics (null vs empty string) and edge cases for SVG anchors.
    • Solid router rendering changes: createLink using Dynamic and Link always rendering <a> — verify prop forwarding, typing, and event handling.
    • E2E tests: reliability of DOMContentLoaded detection and test flakiness across browsers.

Possibly related PRs

Suggested reviewers

  • schiller-manuel
  • birkskyum

Poem

🐰 I hopped upon an SVG bright,
I peeked its target in the light.
No page reload, the route went through—
A tiny hop, a click, and woo! 🥕✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The PR title "Fix React router Link component in SVG elements" accurately describes a primary objective and key change in the changeset. The title clearly identifies the specific problem being addressed (Link component in SVG elements) and the solution scope (React router), with no misleading or off-topic language. While the PR also applies the same fix to the Solid router package and adds comprehensive e2e tests, the React router component fix is the core technical issue that was reported and serves as the main catalyst for this PR. The title is concise, specific, and allows teammates scanning history to understand the primary fix without ambiguity.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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 and usage tips.

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: 0

🧹 Nitpick comments (1)
packages/react-router/src/link.tsx (1)

246-248: Fix correctly addresses the SVG navigation bug.

The change from e.currentTarget.target to getAttribute('target') properly handles both HTML and SVG anchor elements. This resolves the issue where element.target returns an SVGAnimatedString for SVG elements, which was preventing navigation.

Optional: The type assertion can be removed.

Since React.MouseEvent defaults to React.MouseEvent<Element>, e.currentTarget is already typed as EventTarget & Element, and Element.prototype.getAttribute exists. The cast to HTMLAnchorElement | SVGAElement is unnecessary:

-    const elementTarget = (
-      e.currentTarget as HTMLAnchorElement | SVGAElement
-    ).getAttribute('target')
+    const elementTarget = e.currentTarget.getAttribute('target')
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 91d9940 and 4fdb9d0.

📒 Files selected for processing (1)
  • packages/react-router/src/link.tsx (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use TypeScript in strict mode with extensive type safety across the codebase

Files:

  • packages/react-router/src/link.tsx
packages/{react-router,solid-router}/**

📄 CodeRabbit inference engine (AGENTS.md)

Implement React and Solid bindings/components only in packages/react-router/ and packages/solid-router/

Files:

  • packages/react-router/src/link.tsx

@nx-cloud
Copy link

nx-cloud bot commented Oct 24, 2025

View your CI Pipeline Execution ↗ for commit 729b191

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

☁️ Nx Cloud last updated this comment at 2025-10-26 13:41:19 UTC

Copy link
Member

@SeanCassiere SeanCassiere left a comment

Choose a reason for hiding this comment

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

Could you please apply this on the Solid side please? The implementation should mostly be the same.

@pkg-pr-new
Copy link

pkg-pr-new bot commented Oct 24, 2025

More templates

@tanstack/arktype-adapter

npm i https://pkg.pr.new/TanStack/router/@tanstack/arktype-adapter@5590

@tanstack/directive-functions-plugin

npm i https://pkg.pr.new/TanStack/router/@tanstack/directive-functions-plugin@5590

@tanstack/eslint-plugin-router

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

@tanstack/history

npm i https://pkg.pr.new/TanStack/router/@tanstack/history@5590

@tanstack/nitro-v2-vite-plugin

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

@tanstack/react-router

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

@tanstack/react-router-devtools

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

@tanstack/react-router-ssr-query

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

@tanstack/react-start

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-start@5590

@tanstack/react-start-client

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-start-client@5590

@tanstack/react-start-server

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-start-server@5590

@tanstack/router-cli

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

@tanstack/router-core

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

@tanstack/router-devtools

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

@tanstack/router-devtools-core

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

@tanstack/router-generator

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

@tanstack/router-plugin

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

@tanstack/router-ssr-query-core

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

@tanstack/router-utils

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

@tanstack/router-vite-plugin

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

@tanstack/server-functions-plugin

npm i https://pkg.pr.new/TanStack/router/@tanstack/server-functions-plugin@5590

@tanstack/solid-router

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

@tanstack/solid-router-devtools

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

@tanstack/solid-router-ssr-query

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

@tanstack/solid-start

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-start@5590

@tanstack/solid-start-client

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-start-client@5590

@tanstack/solid-start-server

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-start-server@5590

@tanstack/start-client-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-client-core@5590

@tanstack/start-plugin-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-plugin-core@5590

@tanstack/start-server-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-server-core@5590

@tanstack/start-static-server-functions

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

@tanstack/start-storage-context

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-storage-context@5590

@tanstack/valibot-adapter

npm i https://pkg.pr.new/TanStack/router/@tanstack/valibot-adapter@5590

@tanstack/virtual-file-routes

npm i https://pkg.pr.new/TanStack/router/@tanstack/virtual-file-routes@5590

@tanstack/zod-adapter

npm i https://pkg.pr.new/TanStack/router/@tanstack/zod-adapter@5590

commit: 729b191

@schiller-manuel
Copy link
Contributor

can you please also add an e2e test?

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: 0

🧹 Nitpick comments (2)
e2e/react-router/basic/tests/app.spec.ts (1)

53-64: Consider simplifying to match existing test patterns.

The test correctly addresses the reviewer's request and the logic is sound. However, it's inconsistent with other tests in this file:

  1. Manual URL construction (line 60): Other tests rely on implicit navigation without waitForURL. Consider removing the manual URL construction and following the pattern of existing tests (e.g., lines 11-15).
  2. Explicit URL wait: The waitForURL with a full URL is unnecessary if Playwright's baseURL is configured. You could either navigate and assert content on the /posts page, or use a relative URL: await page.waitForURL('/posts').

Apply this diff to align with existing patterns:

 test('Link in SVG does not trigger a full page reload', async ({ page }) => {
   let fullPageLoad = false
   page.on('domcontentloaded', () => {
     fullPageLoad = true
   })
 
   await page.getByRole('link', { name: 'Open posts from SVG' }).click()
-  const url = `http://localhost:${PORT}/posts`
-  await page.waitForURL(url)
+  await page.waitForURL('/posts')
 
   expect(fullPageLoad).toBeFalsy()
 })
e2e/react-router/basic/src/main.tsx (1)

73-88: Remove unused id attribute from title element.

The SVG structure correctly demonstrates the fix for Link components in SVG. However, the id="rectTitle" on line 75 is unused—it's not referenced by any aria-labelledby or other attribute. Since the Link already has an appropriate aria-label, the id serves no purpose and should be removed to avoid confusion.

Apply this diff:

-        <svg width="20" height="20" viewBox="0 0 20 20" role="img">
-          <title id="rectTitle">Link in SVG</title>
+        <svg width="20" height="20" viewBox="0 0 20 20" role="img" aria-label="Link in SVG">
+          <title>Link in SVG</title>
           <Link to="/posts" aria-label="Open posts from SVG">
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4fdb9d0 and b0ec93e.

📒 Files selected for processing (2)
  • e2e/react-router/basic/src/main.tsx (1 hunks)
  • e2e/react-router/basic/tests/app.spec.ts (2 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use TypeScript in strict mode with extensive type safety across the codebase

Files:

  • e2e/react-router/basic/src/main.tsx
  • e2e/react-router/basic/tests/app.spec.ts
e2e/**

📄 CodeRabbit inference engine (AGENTS.md)

Store end-to-end tests under the e2e/ directory

Files:

  • e2e/react-router/basic/src/main.tsx
  • e2e/react-router/basic/tests/app.spec.ts
🧬 Code graph analysis (1)
e2e/react-router/basic/tests/app.spec.ts (1)
scripts/set-ts-version.js (1)
  • packageJson (33-33)
🔇 Additional comments (1)
e2e/react-router/basic/tests/app.spec.ts (1)

2-5: LGTM! Clean test setup.

The JSON import with import attributes and top-level await for PORT is well-structured and follows modern JavaScript patterns.

@dsdeur
Copy link
Author

dsdeur commented Oct 25, 2025

React

I’ve added an e2e test for the React version to the basic example. For detecting the page reload I used the same method as in redirect.spec.ts.

Solid

I’ve also looked into Solid and this turned out to be more tricky than the React version, mainly because of two quirks in Solid:

As you can see in the examples, these are not specific to Tanstack router.

I have prepared a fix for the original issue, but it required a bit of a workaround in the e2e test because of the mentioned issues. For now I have kept this separate so you can decide if you want to include it or not: https://github.com/dsdeur/tanstack-router/pull/1/files

Just let me know, and I can include it in this PR or open a new one.

@schiller-manuel
Copy link
Contributor

@brenelz @birkskyum can you please have a look at the solid part here?

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: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/solid-router/src/link.tsx (1)

1-627: Remove boolean _asChild usage in e2e test and clarify component requirement.

The e2e test at e2e/solid-router/basic/src/main.tsx:77 uses a bare _asChild attribute, which in JSX evaluates to true (boolean). However, the Link component's runtime code (line 592-594 in packages/solid-router/src/link.tsx) passes local._asChild directly to <Dynamic component={...}>, which expects a component or tag name, not a boolean. This will cause a runtime error.

Additionally, the type definition at line 481 incorrectly allows boolean | Solid.ValidComponent, but the implementation cannot handle boolean values. The type should be updated to only accept Solid.ValidComponent (or remove the boolean union entirely).

Changes needed:

  • e2e/solid-router/basic/src/main.tsx:77 — Remove the bare _asChild attribute or pass an actual component/element reference
  • packages/solid-router/src/link.tsx:481 — Update type definition to remove the boolean option from _asChild
🧹 Nitpick comments (4)
e2e/solid-router/basic/tests/app.spec.ts (2)

2-6: Avoid hard-coding origin/port in tests.

Rely on a relative URL or regex to reduce flakiness and remove the PORT dependency.

Apply:

-  const url = `http://localhost:${PORT}/posts`
-  await page.waitForURL(url)
+  await page.waitForURL(/\/posts$/)

53-64: Make reload detection precise and avoid races.

Use once instead of on, and await URL + click concurrently.

Apply:

-  let fullPageLoad = false
-  page.on('domcontentloaded', () => {
-    fullPageLoad = true
-  })
-
-  await page.getByRole('link', { name: 'Open posts from SVG' }).click()
-  const url = `http://localhost:${PORT}/posts`
-  await page.waitForURL(url)
+  let fullPageLoad = false
+  page.once('domcontentloaded', () => {
+    fullPageLoad = true
+  })
+
+  await Promise.all([
+    page.waitForURL(/\/posts$/),
+    page.getByRole('link', { name: 'Open posts from SVG' }).click(),
+  ])
packages/solid-router/src/link.tsx (2)

577-589: Expose real transitioning state to render-prop children.

You’re returning isTransitioning: false. Wire the actual signal.

Apply:

-      return ch({
+      return ch({
         get isActive() {
           return (linkProps as any)['data-status'] === 'active'
         },
-        isTransitioning: false,
+        get isTransitioning() {
+          // hoist the signal from useLinkProps scope if needed
+          return (linkProps as any)['data-transitioning'] === 'transitioning'
+        },
       })

Or, if available in scope, pass isTransitioning() directly.


600-604: Comment suggests SVG anchors won’t work by default; add note about _asChild usage.

Clarify in the comment that _asChild="a" inside <svg> is the supported path, and default <a> only works in HTML.

Proposed:

-  // Default rendering - just use regular anchor element
-  // This works for HTML contexts; SVG links inside SVGs won't work properly
-  // due to Solid's compile-time namespace determination
+  // Default rendering - regular anchor element (HTML context only).
+  // For SVG, prefer `<Link _asChild="a">...</Link>` inside `<svg>`.
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b0ec93e and b451f10.

📒 Files selected for processing (3)
  • e2e/solid-router/basic/src/main.tsx (1 hunks)
  • e2e/solid-router/basic/tests/app.spec.ts (2 hunks)
  • packages/solid-router/src/link.tsx (3 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use TypeScript in strict mode with extensive type safety across the codebase

Files:

  • packages/solid-router/src/link.tsx
  • e2e/solid-router/basic/src/main.tsx
  • e2e/solid-router/basic/tests/app.spec.ts
packages/{react-router,solid-router}/**

📄 CodeRabbit inference engine (AGENTS.md)

Implement React and Solid bindings/components only in packages/react-router/ and packages/solid-router/

Files:

  • packages/solid-router/src/link.tsx
e2e/**

📄 CodeRabbit inference engine (AGENTS.md)

Store end-to-end tests under the e2e/ directory

Files:

  • e2e/solid-router/basic/src/main.tsx
  • e2e/solid-router/basic/tests/app.spec.ts
🔇 Additional comments (1)
packages/solid-router/src/link.tsx (1)

284-287: Good fix: read target via getAttribute for SVG.

This avoids SVGAnimatedString pitfalls and matches browser behavior. LGTM.

@birkskyum birkskyum force-pushed the fix-svg-a-link-react-component branch from b451f10 to 54f23e6 Compare October 26, 2025 00:41
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: 0

🧹 Nitpick comments (1)
e2e/react-router/basic/tests/app.spec.ts (1)

2-5: Consider using relative URL to avoid PORT import.

The PORT constant is only used in the new test's waitForURL call. Since other tests use relative URLs (e.g., page.goto('/') in beforeEach), Playwright appears to be configured with a base URL. You can simplify by using page.waitForURL('/posts') instead of the absolute URL, eliminating the need for these imports.

Apply this diff to use a relative URL:

-import { getTestServerPort } from '@tanstack/router-e2e-utils'
-import packageJson from '../package.json' with { type: 'json' }
-
-const PORT = await getTestServerPort(packageJson.name)
-
 test.beforeEach(async ({ page }) => {

And update the test:

   await page.getByRole('link', { name: 'Open posts from SVG' }).click()
-  const url = `http://localhost:${PORT}/posts`
-  await page.waitForURL(url)
+  await page.waitForURL('/posts')
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b451f10 and 54f23e6.

📒 Files selected for processing (5)
  • e2e/react-router/basic/src/main.tsx (1 hunks)
  • e2e/react-router/basic/tests/app.spec.ts (2 hunks)
  • e2e/solid-router/basic/src/main.tsx (2 hunks)
  • e2e/solid-router/basic/tests/app.spec.ts (2 hunks)
  • packages/react-router/src/link.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • e2e/solid-router/basic/tests/app.spec.ts
  • e2e/solid-router/basic/src/main.tsx
  • packages/react-router/src/link.tsx
  • e2e/react-router/basic/src/main.tsx
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use TypeScript in strict mode with extensive type safety across the codebase

Files:

  • e2e/react-router/basic/tests/app.spec.ts
e2e/**

📄 CodeRabbit inference engine (AGENTS.md)

Store end-to-end tests under the e2e/ directory

Files:

  • e2e/react-router/basic/tests/app.spec.ts
🔇 Additional comments (1)
e2e/react-router/basic/tests/app.spec.ts (1)

53-64: Excellent test coverage for the SVG link fix!

The test correctly verifies that clicking a link inside an SVG element uses client-side routing instead of triggering a full page reload. The domcontentloaded event listener pattern effectively detects whether a full page reload occurred.

@birkskyum birkskyum force-pushed the fix-svg-a-link-react-component branch from 54f23e6 to efcfb61 Compare October 26, 2025 01:30
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: 0

🧹 Nitpick comments (1)
packages/react-router/src/link.tsx (1)

246-249: Excellent fix for the SVGAnimatedString issue!

The use of getAttribute('target') correctly handles both HTMLAnchorElement and SVGAElement, returning null | string instead of the problematic SVGAnimatedString object. This ensures consistent behavior for SVG-based links.

The implementation is sound and maintains backward compatibility.

Optional: Add a clarifying comment.

Consider adding a brief comment explaining why getAttribute is used instead of direct property access to help future maintainers:

-    // Check actual element's target attribute as fallback
+    // Use getAttribute to avoid SVGAnimatedString on SVGAElement.target
     const elementTarget = (
       e.currentTarget as HTMLAnchorElement | SVGAElement
     ).getAttribute('target')
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 54f23e6 and ee74dfc.

📒 Files selected for processing (6)
  • e2e/react-router/basic/src/main.tsx (1 hunks)
  • e2e/react-router/basic/tests/app.spec.ts (2 hunks)
  • e2e/solid-router/basic/src/main.tsx (2 hunks)
  • e2e/solid-router/basic/tests/app.spec.ts (2 hunks)
  • packages/react-router/src/link.tsx (1 hunks)
  • packages/solid-router/src/link.tsx (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • e2e/react-router/basic/src/main.tsx
  • e2e/react-router/basic/tests/app.spec.ts
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use TypeScript in strict mode with extensive type safety across the codebase

Files:

  • e2e/solid-router/basic/tests/app.spec.ts
  • packages/react-router/src/link.tsx
  • e2e/solid-router/basic/src/main.tsx
  • packages/solid-router/src/link.tsx
e2e/**

📄 CodeRabbit inference engine (AGENTS.md)

Store end-to-end tests under the e2e/ directory

Files:

  • e2e/solid-router/basic/tests/app.spec.ts
  • e2e/solid-router/basic/src/main.tsx
packages/{react-router,solid-router}/**

📄 CodeRabbit inference engine (AGENTS.md)

Implement React and Solid bindings/components only in packages/react-router/ and packages/solid-router/

Files:

  • packages/react-router/src/link.tsx
  • packages/solid-router/src/link.tsx
🧬 Code graph analysis (3)
e2e/solid-router/basic/tests/app.spec.ts (1)
scripts/set-ts-version.js (1)
  • packageJson (33-33)
e2e/solid-router/basic/src/main.tsx (2)
packages/solid-router/src/index.tsx (2)
  • Link (229-229)
  • useLinkProps (229-229)
packages/solid-router/src/link.tsx (1)
  • useLinkProps (31-444)
packages/solid-router/src/link.tsx (1)
packages/react-router/src/link.tsx (3)
  • useLinkProps (40-385)
  • Link (567-598)
  • LinkComponent (498-509)
🔇 Additional comments (8)
packages/solid-router/src/link.tsx (4)

2-2: LGTM!

The Dynamic import is required for the new createLink implementation and is correctly imported from solid-js/web.


284-286: Excellent fix for SVG link navigation.

Using getAttribute('target') correctly avoids the SVGAnimatedString issue on SVGAElement while maintaining compatibility with HTMLAnchorElement. The type union properly covers both cases, and this approach aligns with the React implementation.


563-574: LGTM!

The createLink implementation correctly uses Dynamic to support custom components. The prop splitting and type removal are handled properly.


577-603: Link simplified; SVG usage requires useLinkProps workaround.

The removal of _asChild support resolves the runtime errors flagged in past reviews. The plain <a> rendering is correct for HTML contexts.

The comments accurately document that SVG links won't work due to Solid's compile-time namespace determination—the <a> JSX inside Link is compiled in the HTML namespace regardless of where Link is used. The e2e test demonstrates the correct workaround: use useLinkProps directly and render the <a> element in your own JSX inside the <svg>, ensuring proper SVG namespace compilation.

e2e/solid-router/basic/tests/app.spec.ts (2)

2-5: LGTM!

The test setup correctly uses the e2e utilities to dynamically resolve the test server port, enabling proper URL construction in the test.


53-64: Effective test coverage for the SVG link fix.

The test correctly validates that clicking the SVG-embedded link navigates via the router without triggering a full page reload. The domcontentloaded event tracking is a reliable indicator of full page loads, and the aria-label selector properly targets the test element.

e2e/solid-router/basic/src/main.tsx (2)

12-12: LGTM!

The useLinkProps import is required for the SVG link implementation and is correctly imported from @tanstack/solid-router.


75-96: Excellent demonstration of the SVG link workaround.

This implementation correctly addresses Solid's compile-time namespace constraint by:

  1. Using useLinkProps to obtain navigation props (including the fixed onClick handler)
  2. Creating the <a> element directly in your JSX inside the <svg>, ensuring Solid compiles it with the SVG namespace
  3. Spreading the link props onto the <a> element

The accessibility attributes (title, aria-label) are properly included, and this pattern serves as the recommended approach for SVG links when the Link component's HTML-namespace limitation applies.

@birkskyum birkskyum force-pushed the fix-svg-a-link-react-component branch from ee74dfc to 729b191 Compare October 26, 2025 02:35
@birkskyum
Copy link
Member

birkskyum commented Oct 26, 2025

This is apparently much harder to do in solid than react - I've opened a ticket for it, but I think we can fix it for react now, we should just go ahead, and we'll add solid tests when we have a solution for it.

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.

4 participants