Skip to content

fix: preserve search params in canonical URL on stateful routes#864

Merged
tannerlinsley merged 2 commits intomainfrom
claude/fix-canonical-url-params-Kxf8f
Apr 27, 2026
Merged

fix: preserve search params in canonical URL on stateful routes#864
tannerlinsley merged 2 commits intomainfrom
claude/fix-canonical-url-params-Kxf8f

Conversation

@tannerlinsley
Copy link
Copy Markdown
Member

@tannerlinsley tannerlinsley commented Apr 27, 2026

Summary

The npm stats pages (and several other tools/dashboards) encode their entire UI state in URL search params, but __root.tsx built the canonical link, og:url, and twitter:url from pathname only. iOS Share read those tags and shared a bare URL, dropping the user's configured view (e.g. selected packages, range, filters).

  • canonicalUrl(path, search?) now optionally appends a search string (src/utils/seo.ts).
  • __root.tsx reads location.searchStr and uses useMatches to check for staticData.includeSearchInCanonical === true; when set, the search string is threaded through the canonical link, og:url, and twitter:url.
  • StaticDataRouteOption in src/router.tsx augmented with includeSearchInCanonical?: boolean.
  • Flag enabled on routes whose state lives in search params:
    • /stats/npm/, /stats/npm/$packages, /$libraryId/$version/docs/npm-stats
    • /builder
    • /maintainers
    • /partners
    • /intent/registry
    • /shop, /shop/search, /shop/collections/$handle

Routes whose only search params are incidental (pagination, UI prefs) are intentionally left as-is to keep their canonical URLs SEO-clean.

Test plan

  • Visit /stats/npm, configure a non-default package comparison, view source — confirm <link rel="canonical">, og:url, twitter:url all include the ?packageGroups=… search params
  • Repeat on /builder with a non-default config
  • Visit a plain page like / — confirm canonical URL is unchanged (no spurious search params)
  • iOS Share from /stats/npm?packageGroups=… now produces a URL that loads the same comparison

https://claude.ai/code/session_01VbTHG1oRfPsECJ8pcr3qj8


Generated by Claude Code

Summary by CodeRabbit

Release Notes

  • New Features
    • Canonical URLs now optionally include search parameters for improved SEO and page identification on filterable pages (shop, stats, partners, builder, and related routes).

claude added 2 commits April 26, 2026 16:05
The npm stats pages encode their entire UI state (selected packages,
range, transform, etc.) in search params, but __root.tsx built the
canonical link, og:url, and twitter:url from pathname only. iOS Share
read those tags and shared a bare /stats/npm URL, dropping the
comparison the user had configured.

Routes can now opt in via staticData.includeSearchInCanonical; when
set, the current searchStr is appended to the canonical URL. Applied
to /stats/npm/, /stats/npm/$packages, and the per-library
/$libraryId/$version/docs/npm-stats route.
Extends the includeSearchInCanonical opt-in to additional routes
whose UI state lives in search params, so iOS Share and other clients
that read the canonical/og:url/twitter:url tags share the user's
configured view rather than a default one.

- /builder (entire builder config: framework, features, tab, file…)
- /maintainers (libraries, viewMode, groupBy, sortBy)
- /partners (libraries, status)
- /intent/registry (q, tab, framework, sort, view)
- /shop, /shop/search, /shop/collections/$handle
@netlify
Copy link
Copy Markdown

netlify Bot commented Apr 27, 2026

Deploy Preview for tanstack ready!

Name Link
🔨 Latest commit b815f3f
🔍 Latest deploy log https://app.netlify.com/projects/tanstack/deploys/69eecae04d70a500099c93cf
😎 Deploy Preview https://deploy-preview-864--tanstack.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
Lighthouse
Lighthouse
1 paths audited
Performance: 44 (🔴 down 6 from production)
Accessibility: 90 (no change from production)
Best Practices: 83 (🔴 down 9 from production)
SEO: 97 (no change from production)
PWA: 70 (no change from production)
View the detailed breakdown and full score reports

To edit notification comments on pull requests, go to your Netlify project configuration.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 27, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8a8569da-d43d-4abf-b5b9-b92a1474e06e

📥 Commits

Reviewing files that changed from the base of the PR and between 1e63ccf and b815f3f.

📒 Files selected for processing (13)
  • src/router.tsx
  • src/routes/$libraryId/$version.docs.npm-stats.tsx
  • src/routes/__root.tsx
  • src/routes/builder.index.tsx
  • src/routes/intent/registry/index.tsx
  • src/routes/maintainers.tsx
  • src/routes/partners.index.tsx
  • src/routes/shop.collections.$handle.tsx
  • src/routes/shop.index.tsx
  • src/routes/shop.search.tsx
  • src/routes/stats/npm/$packages.tsx
  • src/routes/stats/npm/index.tsx
  • src/utils/seo.ts

📝 Walkthrough

Walkthrough

This change implements optional search parameter inclusion in canonical URLs across the application. It adds a new includeSearchInCanonical property to TanStack React Router's route configuration, updates the root component to conditionally capture and apply search strings, extends the canonical URL utility function to accept search parameters, and configures multiple routes to enable this behavior.

Changes

Cohort / File(s) Summary
Type Definition
src/router.tsx
Adds optional includeSearchInCanonical?: boolean property to StaticDataRouteOption type via module augmentation.
Canonical URL Logic
src/routes/__root.tsx
Captures current search string and conditionally includes it in canonical URL generation based on matched route's static data configuration.
Canonical URL Utility
src/utils/seo.ts
Updates canonicalUrl function signature to accept optional search parameter and incorporates it into the returned canonical URL string.
Route Configurations
src/routes/$libraryId/$version.docs.npm-stats.tsx, src/routes/builder.index.tsx, src/routes/intent/registry/index.tsx, src/routes/maintainers.tsx, src/routes/partners.index.tsx, src/routes/shop.collections.$handle.tsx, src/routes/shop.index.tsx, src/routes/shop.search.tsx, src/routes/stats/npm/$packages.tsx, src/routes/stats/npm/index.tsx
Enables search parameter inclusion in canonical URLs by setting staticData: { includeSearchInCanonical: true } for 10 routes.

Sequence Diagram

sequenceDiagram
    participant Router as TanStack Router
    participant ShellComp as Root Shell Component
    participant SEOUtil as canonicalUrl Utility
    
    Router->>ShellComp: Render with matched route & search params
    ShellComp->>ShellComp: Capture location.searchStr
    ShellComp->>ShellComp: Check route.staticData.includeSearchInCanonical
    alt includeSearchInCanonical is true
        ShellComp->>SEOUtil: canonicalUrl(path, searchStr)
    else includeSearchInCanonical is false/undefined
        ShellComp->>SEOUtil: canonicalUrl(path, '')
    end
    SEOUtil->>SEOUtil: Normalize path and search
    SEOUtil->>ShellComp: Return full canonical URL
    ShellComp->>ShellComp: Set <link rel="canonical"> href
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 Hop, hop—a quest for clarity!
Search strings now grace the canonical way,
Routes declare their preference with glee,
URLs complete, both short and... searchfully gray!

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/fix-canonical-url-params-Kxf8f

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

@tannerlinsley tannerlinsley merged commit 5194bcd into main Apr 27, 2026
6 of 8 checks passed
@tannerlinsley tannerlinsley deleted the claude/fix-canonical-url-params-Kxf8f branch April 27, 2026 02:34
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