{"_id":"@demershov/use-route-query","_rev":"5-d84e2c30ace40d00cc53122c0f626a82","name":"@demershov/use-route-query","dist-tags":{"latest":"0.0.5"},"versions":{"0.0.1":{"name":"@demershov/use-route-query","version":"0.0.1","author":{"url":"https://github.com/demershov","name":"Dima Ershov"},"license":"MIT","_id":"@demershov/use-route-query@0.0.1","maintainers":[{"name":"demershov","email":"demeershov@gmail.com"}],"homepage":"https://github.com/demershov/useRouteQuery#readme","bugs":{"url":"https://github.com/demershov/useRouteQuery/issues"},"dist":{"shasum":"4f7a989107aba3abffe8b75eeec70ae25a23c689","tarball":"https://registry.npmjs.org/@demershov/use-route-query/-/use-route-query-0.0.1.tgz","fileCount":5,"integrity":"sha512-0h3tvfJZJSn09ULTNkM/FIR0mYpt8/gFtkqTfYPWNhDl2rvzKIuuvU/YvaB5s9wtThGZrosU8yLQ/FTgPwpDbQ==","signatures":[{"sig":"MEUCIBaz9GUUzEVFbp+Nt+0eFoFjPlUhtsDBddsRJ1DZvp4VAiEAxrbrAzM2aaNGZkyelPNqSPGcTJUogbQowJ7otQys6y8=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":11757},"main":"./dist/useRouteQuery.js","type":"module","types":"./dist/useRouteQuery.d.ts","module":"./dist/useRouteQuery.js","engines":{"node":">=22.0.0"},"exports":{".":"./dist/useRouteQuery.js","./package.json":"./package.json"},"gitHead":"f5df3609747b408162fd5bbfd654b3b21ef15c98","scripts":{"dev":"tsdown --watch","test":"vitest","build":"tsdown","coverage":"vitest run --coverage","typecheck":"tsc --noEmit","lint:package":"publint"},"_npmUser":{"name":"demershov","email":"demeershov@gmail.com"},"repository":{"url":"git+https://github.com/demershov/useRouteQuery.git","type":"git"},"_npmVersion":"10.9.2","description":"A Vue 3 composable for synchronizing route query parameters with reactive state.","directories":{},"_nodeVersion":"22.14.0","publishConfig":{"exports":{".":"./dist/useRouteQuery.js","./package.json":"./package.json"}},"_hasShrinkwrap":false,"packageManager":"pnpm@9.15.0+sha1.8bfdb6d72b4d5fdf87d21d27f2bfbe2b21dd2629","devDependencies":{"vue":"^3.5.22","jiti":"^2.6.1","bumpp":"^10.3.1","eslint":"^9.38.0","tsdown":"^0.15.9","vitest":"^4.0.1","globals":"^16.4.0","publint":"^0.3.15","prettier":"3.6.2","@eslint/js":"^9.38.0","typescript":"^5.9.3","vue-router":"^4.6.3","@types/node":"^24.9.1","typescript-eslint":"^8.46.2","@vitest/coverage-v8":"^4.0.5"},"peerDependencies":{"vue":"^3.5.22","vue-router":"^4.6.3"},"_npmOperationalInternal":{"tmp":"tmp/use-route-query_0.0.1_1761995870838_0.42587004258038674","host":"s3://npm-registry-packages-npm-production"}},"0.0.2":{"name":"@demershov/use-route-query","version":"0.0.2","author":{"url":"https://github.com/demershov","name":"Dima Ershov"},"license":"MIT","_id":"@demershov/use-route-query@0.0.2","maintainers":[{"name":"demershov","email":"demeershov@gmail.com"}],"homepage":"https://github.com/demershov/useRouteQuery#readme","bugs":{"url":"https://github.com/demershov/useRouteQuery/issues"},"dist":{"shasum":"478e5eb6b0765fdbf2214836eb15645d7a369b44","tarball":"https://registry.npmjs.org/@demershov/use-route-query/-/use-route-query-0.0.2.tgz","fileCount":5,"integrity":"sha512-2YdJMNV982iTSqI7Ih/66F56Xn/N3EylXkseQB37KzrkjDnDnShtJtK9g5HPvasOZrFt1csPNV4sl4IpRFhJwQ==","signatures":[{"sig":"MEUCICbEQUFn36HWN3BTndbLrvY4wbiaVAghZp2zFtyhX1fAAiEAmkk+SAAqhwgTlWqnTr7UjLoNFNco1+4rvKwGDmNiTEk=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":11547},"main":"./dist/useRouteQuery.js","type":"module","_from":"file:demershov-use-route-query-0.0.2.tgz","types":"./dist/useRouteQuery.d.ts","module":"./dist/useRouteQuery.js","engines":{"node":">=22.0.0"},"exports":{".":"./dist/useRouteQuery.js","./package.json":"./package.json"},"scripts":{"dev":"tsdown --watch","test":"vitest","build":"tsdown","coverage":"vitest run --coverage","typecheck":"tsc --noEmit","lint:package":"publint"},"_npmUser":{"name":"demershov","email":"demeershov@gmail.com"},"_resolved":"/tmp/08f97420bf042f19f1cdc8c64349fb4f/demershov-use-route-query-0.0.2.tgz","_integrity":"sha512-2YdJMNV982iTSqI7Ih/66F56Xn/N3EylXkseQB37KzrkjDnDnShtJtK9g5HPvasOZrFt1csPNV4sl4IpRFhJwQ==","repository":{"url":"git+https://github.com/demershov/useRouteQuery.git","type":"git"},"_npmVersion":"10.9.2","description":"A Vue 3 composable for synchronizing route query parameters with reactive state.","directories":{},"_nodeVersion":"22.14.0","_hasShrinkwrap":false,"devDependencies":{"vue":"^3.5.22","jiti":"^2.6.1","bumpp":"^10.3.1","eslint":"^9.38.0","tsdown":"^0.15.9","vitest":"^4.0.1","globals":"^16.4.0","publint":"^0.3.15","prettier":"3.6.2","@eslint/js":"^9.38.0","typescript":"^5.9.3","vue-router":"^4.6.3","@types/node":"^24.9.1","typescript-eslint":"^8.46.2","@vitest/coverage-v8":"^4.0.5"},"peerDependencies":{"vue":"^3.5.22","vue-router":"^4.6.3"},"_npmOperationalInternal":{"tmp":"tmp/use-route-query_0.0.2_1762006395678_0.4008683681609133","host":"s3://npm-registry-packages-npm-production"}},"0.0.3":{"name":"@demershov/use-route-query","version":"0.0.3","author":{"url":"https://github.com/demershov","name":"Dima Ershov"},"license":"MIT","_id":"@demershov/use-route-query@0.0.3","maintainers":[{"name":"demershov","email":"demeershov@gmail.com"}],"homepage":"https://github.com/demershov/useRouteQuery#readme","bugs":{"url":"https://github.com/demershov/useRouteQuery/issues"},"dist":{"shasum":"230a99bddc80ab5fff2149dde04d165b7caa954f","tarball":"https://registry.npmjs.org/@demershov/use-route-query/-/use-route-query-0.0.3.tgz","fileCount":6,"integrity":"sha512-lbVmFvLPFalmyQEpFcs3F2QVtFyhxNaXu64vysb42YZXeCqomkonMD6ciFjgyZhvv46jqRU1IQQq0O2GwfpgHg==","signatures":[{"sig":"MEUCIQC1mOVEuY7WfpxWbHenSuJzJoCnNuBe1yQ7h68JCOmkAQIgKdTFpDwzxWwOEC/yqtpF+qRPJjEyT9d1bt0mOKxvmz0=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":17052},"main":"./dist/useRouteQuery.js","type":"module","_from":"file:demershov-use-route-query-0.0.3.tgz","types":"./dist/useRouteQuery.d.ts","module":"./dist/useRouteQuery.js","engines":{"node":">=22.0.0"},"exports":{".":"./dist/useRouteQuery.js","./package.json":"./package.json"},"scripts":{"dev":"tsdown --watch","test":"vitest","build":"tsdown","coverage":"vitest run --coverage","typecheck":"tsc --noEmit","lint:package":"publint"},"_npmUser":{"name":"demershov","email":"demeershov@gmail.com"},"_resolved":"/tmp/45c8abb930a2a885a820dc26c6d0bd61/demershov-use-route-query-0.0.3.tgz","_integrity":"sha512-lbVmFvLPFalmyQEpFcs3F2QVtFyhxNaXu64vysb42YZXeCqomkonMD6ciFjgyZhvv46jqRU1IQQq0O2GwfpgHg==","repository":{"url":"git+https://github.com/demershov/useRouteQuery.git","type":"git"},"_npmVersion":"10.9.2","description":"A Vue 3 composable for synchronizing route query parameters with reactive state.","directories":{},"_nodeVersion":"22.14.0","_hasShrinkwrap":false,"devDependencies":{"vue":"^3.5.22","jiti":"^2.6.1","bumpp":"^10.3.1","eslint":"^9.38.0","tsdown":"^0.15.9","vitest":"^4.0.1","globals":"^16.4.0","publint":"^0.3.15","prettier":"3.6.2","@eslint/js":"^9.38.0","typescript":"^5.9.3","vue-router":"^4.6.3","@types/node":"^24.9.1","typescript-eslint":"^8.46.2","@vitest/coverage-v8":"^4.0.5"},"peerDependencies":{"vue":"^3.5.22","vue-router":"^4.6.3"},"_npmOperationalInternal":{"tmp":"tmp/use-route-query_0.0.3_1762008881578_0.18679790124289308","host":"s3://npm-registry-packages-npm-production"}},"0.0.4":{"name":"@demershov/use-route-query","version":"0.0.4","keywords":["vue","composable","router","route-query","query","use-route-query","useRouteQuery"],"author":{"url":"https://github.com/demershov","name":"Dima Ershov"},"license":"MIT","_id":"@demershov/use-route-query@0.0.4","maintainers":[{"name":"demershov","email":"demeershov@gmail.com"}],"homepage":"https://github.com/demershov/useRouteQuery#readme","bugs":{"url":"https://github.com/demershov/useRouteQuery/issues"},"dist":{"shasum":"4a7db8b26b18be19de6cc8df51a05a03bf02296c","tarball":"https://registry.npmjs.org/@demershov/use-route-query/-/use-route-query-0.0.4.tgz","fileCount":6,"integrity":"sha512-OrIBOac/lyKJqn68c9ee3aOxyb5P+2RdGUDqry50tX+58TDCunKTXVS/rT2gZsoEVCwhYPV8frzppdfRgtQSQA==","signatures":[{"sig":"MEQCIATfFVw10tRQ74dNxnLP7nLJkrj9JQfBZNI4cDRNAbOPAiBHUO8hWMBW7noOHRQvdQ5xzUwm7FB1MDd/KrgUk4qtHw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":17191},"main":"./dist/useRouteQuery.js","type":"module","_from":"file:demershov-use-route-query-0.0.4.tgz","types":"./dist/useRouteQuery.d.ts","module":"./dist/useRouteQuery.js","engines":{"node":">=22.0.0"},"exports":{".":"./dist/useRouteQuery.js","./package.json":"./package.json"},"scripts":{"dev":"tsdown --watch","test":"vitest","build":"tsdown","coverage":"vitest run --coverage","typecheck":"tsc --noEmit","lint:package":"publint"},"_npmUser":{"name":"demershov","email":"demeershov@gmail.com"},"_resolved":"/tmp/a8ab86f9ddea935ab551b9186b5c3d57/demershov-use-route-query-0.0.4.tgz","_integrity":"sha512-OrIBOac/lyKJqn68c9ee3aOxyb5P+2RdGUDqry50tX+58TDCunKTXVS/rT2gZsoEVCwhYPV8frzppdfRgtQSQA==","repository":{"url":"git+https://github.com/demershov/useRouteQuery.git","type":"git"},"_npmVersion":"10.9.2","description":"A Vue 3 composable for synchronizing route query parameters with reactive state.","directories":{},"_nodeVersion":"22.14.0","_hasShrinkwrap":false,"devDependencies":{"vue":"^3.5.22","jiti":"^2.6.1","bumpp":"^10.3.1","eslint":"^9.38.0","tsdown":"^0.15.9","vitest":"^4.0.1","globals":"^16.4.0","publint":"^0.3.15","prettier":"3.6.2","@eslint/js":"^9.38.0","typescript":"^5.9.3","vue-router":"^4.6.3","@types/node":"^24.9.1","typescript-eslint":"^8.46.2","@vitest/coverage-v8":"^4.0.5"},"peerDependencies":{"vue":"^3.5.22","vue-router":"^4.6.3"},"_npmOperationalInternal":{"tmp":"tmp/use-route-query_0.0.4_1762009135469_0.5938194265244792","host":"s3://npm-registry-packages-npm-production"}},"0.0.5":{"name":"@demershov/use-route-query","version":"0.0.5","description":"A Vue 3 composable for synchronizing route query parameters with reactive state.","keywords":["vue","composable","router","route-query","query","use-route-query","useRouteQuery"],"type":"module","license":"MIT","homepage":"https://github.com/demershov/useRouteQuery#readme","bugs":{"url":"https://github.com/demershov/useRouteQuery/issues"},"repository":{"type":"git","url":"git+https://github.com/demershov/useRouteQuery.git"},"author":{"name":"Dima Ershov","url":"https://github.com/demershov"},"main":"./dist/useRouteQuery.js","module":"./dist/useRouteQuery.js","types":"./dist/useRouteQuery.d.ts","exports":{".":"./dist/useRouteQuery.js","./package.json":"./package.json"},"engines":{"node":">=22.0.0"},"devDependencies":{"@eslint/js":"^9.38.0","@types/node":"^24.9.1","@vitest/coverage-v8":"^4.0.5","bumpp":"^10.3.1","eslint":"^9.38.0","globals":"^16.4.0","jiti":"^2.6.1","prettier":"3.6.2","publint":"^0.3.15","tsdown":"^0.15.9","typescript":"^5.9.3","typescript-eslint":"^8.46.2","vitest":"^4.0.1","vue":"^3.5.22","vue-router":"^4.6.3"},"peerDependencies":{"vue":"^3.5.22","vue-router":"^4.6.3"},"scripts":{"build":"tsdown","dev":"tsdown --watch","test":"vitest","coverage":"vitest run --coverage","typecheck":"tsc --noEmit","lint:package":"publint"},"_id":"@demershov/use-route-query@0.0.5","_integrity":"sha512-gjucCNe+sJgATj+riu85VGzDcUZqnbdSnVKHs3VCecw3qeNsBCl3O+JT4bWG+KeKs+qisJakrTo/IswmoRP00A==","_resolved":"/tmp/78af41799fe052c07a72bbfd9c2a5902/demershov-use-route-query-0.0.5.tgz","_from":"file:demershov-use-route-query-0.0.5.tgz","_nodeVersion":"22.14.0","_npmVersion":"10.9.2","dist":{"integrity":"sha512-gjucCNe+sJgATj+riu85VGzDcUZqnbdSnVKHs3VCecw3qeNsBCl3O+JT4bWG+KeKs+qisJakrTo/IswmoRP00A==","shasum":"5472d5dee5b8983dff221e783043770b17557695","tarball":"https://registry.npmjs.org/@demershov/use-route-query/-/use-route-query-0.0.5.tgz","fileCount":6,"unpackedSize":17191,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIBstNU1UGbs8C3JDD36+7eIk+cCbuypoCtnqsb6KuqNYAiEAzs6R4lpJY/PehPSa1L0Jfkzd5OpEB/WuUp3XXDDSWvA="}]},"_npmUser":{"name":"demershov","email":"demeershov@gmail.com"},"directories":{},"maintainers":[{"name":"demershov","email":"demeershov@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/use-route-query_0.0.5_1762010014199_0.3408387883965023"},"_hasShrinkwrap":false}},"time":{"created":"2025-11-01T11:17:50.756Z","modified":"2025-11-01T15:13:34.584Z","0.0.1":"2025-11-01T11:17:51.028Z","0.0.2":"2025-11-01T14:13:15.966Z","0.0.3":"2025-11-01T14:54:41.771Z","0.0.4":"2025-11-01T14:58:55.663Z","0.0.5":"2025-11-01T15:13:34.401Z"},"bugs":{"url":"https://github.com/demershov/useRouteQuery/issues"},"author":{"name":"Dima Ershov","url":"https://github.com/demershov"},"license":"MIT","homepage":"https://github.com/demershov/useRouteQuery#readme","keywords":["vue","composable","router","route-query","query","use-route-query","useRouteQuery"],"repository":{"type":"git","url":"git+https://github.com/demershov/useRouteQuery.git"},"description":"A Vue 3 composable for synchronizing route query parameters with reactive state.","maintainers":[{"name":"demershov","email":"demeershov@gmail.com"}],"readme":"# useRouteQuery\n![npm version](https://img.shields.io/npm/v/@demershov/use-route-query.svg)\n\nA small Vue 3 composable for syncing a reactive value with the route query while avoiding unnecessary object churn from transformer functions.\n\nThis library was inspired by `@vueuse/router`'s `useRouteQuery`. The motivating issue is that when a transformer (the `transform` option) returns a non-primitive (an object or array), it may produce a new object instance even if its contents haven't changed. Since Vue compares refs by identity, returning a new object causes downstream components that receive the reactive value as a prop to re-render even when the logical value is unchanged. \n\nThe author found this behavior undesirable and implemented this library to avoid always returning a new object when a transformer produces an object that is semantically equal to the previous one.\n\nAdditional motivation and typing differences:\n\n- In `@vueuse/router` the default value is often treated as a primitive. In contrast, this implementation types `defaultValue` as `T` (matching the value returned by `transform.get`). That allows passing typed defaults (objects or primitives) so the returned `Ref` has a clear `T` type.\n- In this implementation the URL may contain the default value if the user explicitly sets it. That means default values are not implicitly hidden from the URL — if your app writes the default into the query, it will appear in the route.\n\nFor more details and discussion about the underlying problem see:\n\n- https://github.com/vuejs/docs/issues/2884\n- https://github.com/vueuse/vueuse/issues/3992\n\n## Key idea\n\n- Provide a composable `useRouteQuery(name, defaultValue?, options?)` that returns a `Ref<T>`.\n- If you supply `options.transform.get` / `options.transform.set`, the library uses them but keeps an internal cache of the raw query and compares raw values to avoid exposing a new object when the logical value hasn't changed.\n\n## API\n\n```ts\nfunction useRouteQuery<T>(\n  name: string,\n  defaultValue?: MaybeRefOrGetter<T>,\n  options?: RouteQueryOptions<T>\n): Ref<T>\n```\n\n- `name` — query parameter name\n- `defaultValue` — default value when the query param is absent\n- `options` — optional settings:\n  - `mode` — `'push' | 'replace'` (defaults to `'push'`)\n  - `route` — custom route (defaults to `useRoute()`)\n  - `router` — custom router (defaults to `useRouter()`)\n  - `transform` — `RouteQueryTransform<T>`: `{ get?: (value: GetQueryValue) => T, set?: (value: T) => SetQueryValue }`\n\n## Examples\n\nBasic usage (string query):\n\n```ts\nimport { useRouteQuery } from \"useRouteQuery\";\n\nconst q = useRouteQuery(\"q\", \"\");\n// q is a Ref<string>\n\n// read\nconsole.log(q.value);\n\n// write\nq.value = \"search term\";\n```\n\nUsing a transformer for a primitive:\n\n```ts\nimport { useRouteQuery } from \"useRouteQuery\";\n\nconst page = useRouteQuery<number>(\"page\", 1, {\n  transform: {\n    get: (v) => Number(v),\n    set: (n) => String(n),\n  },\n});\n\n// page.value is a number\n```\n\nUsing a transformer that returns an object (safe from unnecessary rerenders):\n\n```ts\nimport { useRouteQuery } from \"useRouteQuery\";\n\nconst filter = useRouteQuery(\"filter\", { text: \"\" }, {\n  transform: {\n    get: (v) => {\n      // parse query string into an object\n      if (!v) return { text: \"\" };\n      try {\n        return JSON.parse(Array.isArray(v) ? v[0] : v);\n      } catch {\n        return { text: String(v) };\n      }\n    },\n    set: (obj) => {\n      try {\n        return JSON.stringify(obj);\n      } catch {\n        return undefined;\n      }\n    },\n  },\n});\n\n// Because this library caches the raw query and checks equality,\n// components won't re-render unnecessarily when the transformer returns\n// a new but equivalent object instance.\n```\n\n### Typed-default examples\n\n```ts\n// typed default object — returned ref has type { text: string }\nconst filter = useRouteQuery(\"filter\", { text: \"\" }, {\n  transform: {\n    get: (v) => {\n      if (!v) return { text: \"\" } as const;\n      try {\n        return JSON.parse(Array.isArray(v) ? v[0] : v);\n      } catch {\n        return { text: String(v) };\n      }\n    },\n    set: (obj) => JSON.stringify(obj),\n  },\n});\n\n// primitive default example — returned ref has type number\nconst page = useRouteQuery<number>(\"page\", 1, {\n  transform: {\n    get: (v) => Number(v),\n    set: (n) => String(n),\n  },\n});\n```\n\n### Default-in-URL behavior (short demo)\n\n```ts\nimport { useRouteQuery } from \"useRouteQuery\";\n\n// Suppose we use a JSON transformer as above\nconst filter = useRouteQuery(\"filter\", { text: \"\" }, {\n  transform: {\n    get: (v) => {\n      if (!v) return { text: \"\" };\n      try {\n        return JSON.parse(Array.isArray(v) ? v[0] : v);\n      } catch {\n        return { text: String(v) };\n      }\n    },\n    set: (obj) => JSON.stringify(obj),\n  },\n});\n\n// If the user writes the default value explicitly, this implementation\n// will write it into the URL (e.g. ?filter=%7B%22text%22%3A%22%22%7D).\nfilter.value = { text: \"\" };\n\n// By contrast, vueuse's `useRouteQuery` will convert a value equal to\n// the default into `undefined` before writing, causing the query param\n// to be omitted from the URL.\n```\n\n## Notes\n\n- This composable intentionally avoids exposing low-level internals. It returns a `Ref<T>` that you can read/write as usual.\n- The library compares raw query values (after `transform.set`) with a cached raw value to avoid enqueuing router updates or triggering ref updates when the effective value is unchanged.\n\n## License\n\nMIT\n","readmeFilename":"README.md"}