Skip to content

fix: encode path params per RFC 3986 path-segment rules in href/generatePath#15310

Merged
brophdawg11 merged 2 commits into
remix-run:mainfrom
nowells:fix/encode-path-params-pchar
Jul 14, 2026
Merged

fix: encode path params per RFC 3986 path-segment rules in href/generatePath#15310
brophdawg11 merged 2 commits into
remix-run:mainfrom
nowells:fix/encode-path-params-pchar

Conversation

@nowells

@nowells nowells commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Follow-up to #15277, which fixed href() to URL-encode param values to match generatePath(). Both now encode with encodeURIComponent, which implements query-string escaping — a stricter rule than URL paths require. This over-escapes characters that RFC 3986 explicitly allows literally in a path segment:

pchar      = unreserved / pct-encoded / sub-delims / ":" / "@"
sub-delims = "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "="

$ & + , ; = : @ only act as delimiters in a query string; in a path segment they carry no special meaning, and browsers keep them literal in location.pathname. Encoding them needlessly rewrites URLs:

href("/releases/:v", { v: "1.0.0+1" })
// before: /releases/1.0.0%2B1
// after:  /releases/1.0.0+1

which breaks apps that compare generated URLs against window.location.pathname (the browser reports the literal +), churns canonical/shareable URLs, and makes href() output disagree with what users see in the address bar.

Changes:

  • Add an internal encodePathParam() that escapes structural/unsafe characters (/ ? # %, whitespace, non-ASCII) exactly as before, but restores the RFC 3986 pchar set that encodeURIComponent over-escapes
  • Use it for named params in generatePath() and for named params and splat segments in href()
  • Document path-segment vs query-string encoding semantics (with the RFC reference) in the href() and generatePath() JSDoc
  • Update and extend unit tests; add a change file

Behavior is unchanged for every character outside $ & + , ; = : @, and generatePath()'s splat values remain un-encoded, as before.

@github-actions

Copy link
Copy Markdown
Contributor

✅ CLA Signed

Thanks for signing the Contributor License Agreement.

@github-actions

Copy link
Copy Markdown
Contributor

✅ Change File Found

One or more change files found.

Type Change
patch Encode path params in href/generatePath per RFC 3986 path-segment rules instead of encodeURIComponent

@nowells nowells force-pushed the fix/encode-path-params-pchar branch 2 times, most recently from c5cf922 to 0773fcb Compare July 14, 2026 02:47
…atePath

Follow-up to remix-run#15277, which fixed `href()` to URL-encode param values to
match `generatePath()`. Both now encode with `encodeURIComponent`, which
implements *query-string* escaping — a stricter rule than URL paths
require. This over-escapes characters that RFC 3986 explicitly allows
literally in a path segment:

    pchar      = unreserved / pct-encoded / sub-delims / ":" / "@"
    sub-delims = "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "="

https://datatracker.ietf.org/doc/html/rfc3986#section-3.3

`$ & + , ; = : @` only act as delimiters in a query string; in a path
segment they carry no special meaning, and browsers keep them literal in
`location.pathname`. Encoding them needlessly rewrites URLs:

    href("/releases/:v", { v: "1.0.0+1" })
    // before: /releases/1.0.0%2B1
    // after:  /releases/1.0.0+1

which breaks apps that compare generated URLs against
`window.location.pathname` (the browser reports the literal `+`), churns
canonical/shareable URLs, and makes `href()` output disagree with what
users see in the address bar.

Changes:

- Add an internal `encodePathParam()` that escapes structural/unsafe
  characters (`/ ? # %`, whitespace, non-ASCII) exactly as before, but
  restores the RFC 3986 pchar set that `encodeURIComponent` over-escapes
- Use it for named params in `generatePath()` and for named params and
  splat segments in `href()`
- Document path-segment vs query-string encoding semantics (with the RFC
  reference) in the `href()` and `generatePath()` JSDoc
- Update and extend unit tests; add a change file

Behavior is unchanged for every character outside `$ & + , ; = : @`, and
`generatePath()`'s splat values remain un-encoded, as before.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@nowells nowells force-pushed the fix/encode-path-params-pchar branch from 0773fcb to 3f9701e Compare July 14, 2026 03:10

@brophdawg11 brophdawg11 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks!

@brophdawg11 brophdawg11 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks!

@brophdawg11 brophdawg11 self-assigned this Jul 14, 2026
@brophdawg11 brophdawg11 merged commit baa9ba6 into remix-run:main Jul 14, 2026
12 checks passed
@ryanflorence ryanflorence mentioned this pull request Jul 14, 2026
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