-
-
Notifications
You must be signed in to change notification settings - Fork 159
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: Sort query string keys #638
Conversation
BREAKING CHANGE: drop CJS support. Since Next has ESM support since v12, it should not really be a breaking change for most.
BREAKING CHANGE: the following deprecated APIs have been removed: - `queryTypes` bag of parsers -> use individual `parseAsXYZ` parsers for better tree-shakeability. - `subscribeToQueryUpdates` helper -> since Next.js 14.0.5, `useSearchParams` is reactive to shallow search params updates in the app router. See 47ng#425. - Internal types and aliases
BREAKING CHANGE: package is now only updating on the `nuqs` name. The debugging printouts no longer check for next-usequerystate, only nuqs.
BREAKING CHANGE: export path has been renamed. Contents are identical. Since the `/parsers` export contained the server cache, this name makes better sense and helps outline the client/server nature of features in nuqs.
BREAKING CHANGE: Drop support for next@14.0.3 Due to a bug in the implementation of shallow routing (WHS), 14.0.3 required a special case for syncing against external navigation. In `nuqs@2.x`, we're cleaning this up and requiring a version of Next.js with bug-free support for shallow routing (with or without experimental WHS in 14.0.4, and with stabilised WHS in 14.0.5 onwards).
Using a hack around the serializer to apply the same encoding as the hooks would in the URL.
Next.js versions between 14.0.2 and 14.1.0 contain bugs on shallow routing (or other routing code) that makes `nuqs` hard to maintain for these versions.
- Follow the Rules of React to init the query ref once on mount - Remove access to the queued value, doesn't seem to change outcome of issue 359
@hugotiger is attempting to deploy a commit to the 47ng Team on Vercel. A member of the Team first needs to authorize it. |
Thanks! Could you rebase it on top of the |
Also, as an FYI, this change may be unexpected for most people: https://x.com/fortysevenfx/status/1835384141808021801 I'll play with this PR once it's ready, and we'll see if it makes sense to merge it as-is or to provide some other kind of API to sort the keys in a one-shot way, or to provide this behaviour as an option. |
3605b98
to
6984607
Compare
6984607
to
71c84cc
Compare
A rough first version is ready now. It's one of my first time contributing to an OSS-project so sorry if I'm missing something. Let me know and I'll fix it 😄 I understand this change might be considered somewhat controversial, especially regarding backward compatibility. Like you're saying, it might be good to have an option to enable/disable it. However, I believe the default behavior should remain sorted. While it may not align with everyone’s expectations, it provides significant SEO benefits, which would likely be valuable to a majority of users. Similar to how this library has some handy opinionated defaults out of the box (replace, reset on null etc.), I believe sorting should be the default as it’s what would be the most practical. |
const query: string[] = [] | ||
for (const [key, value] of search.entries()) { | ||
for (const [key, value] of entries) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
URLSearchParams
is iterable on its own which is neat

A SO answer from a self-described v8 dev on the cost of Object.entries()
https://stackoverflow.com/a/64911507
I am assuming URLSearchParams proxies to Object.entries or is implemented the same way. This feels a bit pedantic, but if encoding happens frequently and with a large amount of parameters, I could imagine this mattering.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried a benchmark and my theory of this helping was dead wrong lol sorry
benchmark (from perf.link)
still is neat to know imo, but please disregard other than the syntax difference
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suppose it's implemented the same way internally.
The main advantage of iterating directly on the URLSearchParams is skipping an extra entries
variable, so saving memory and a few bytes of bundle size.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thinking about this, we should probably mutate the URLSearchParams passed in argument rather than copying, as it will be returned by the Promise of useQueryState(s) state updater functions, and it should reflect the order.
To keep the encoding function pure, the sort call should then happen after the search params have been populated, but before encoding.
This could also allow placing this behaviour behind an option. A good place for it would be the flushUpdateQueue
function in update-queue.ts
.
b7d5bda
to
be08a7c
Compare
972ec59
to
28dfb30
Compare
c281958
to
39b84be
Compare
To avoid SEO issues from duplicate content this PR adds sorting of the query parameters based on their keys. That way the URL should be more predictable and canonicals won't be needed as often.
I went with using the native
sort
method on theURLSearchParams
class to align it with existing standards.As long as no user has depended on the order of the query string this PR shouldn't be breaking.