Query String
The parse/serialize half of every URL-driven feature — filters, sorts, pagination, search forms — packaged as one default export. Pass it an object, get back a query string with [] array brackets and [parent][child] nesting. Pass it a query string, get back an object with numeric coercion applied. Four browser-bound methods (all, get, update, toString) read and write window.location; the two pure data methods (parse, toQueryString) work anywhere.
Highlighted features
Nested object + array shapes
?tags[]=a&user[name]=alice ⇄ { tags: [“a”], user: { name: “alice” } }. Any depth, both directions.
Numeric coercion on parse
”page=2” parses as { page: 2 } — the number, not the string. Lossy by design: “007” → 7.
No-reload URL rewriter
queryString.update({…}) rewrites window.location.search via history.replaceState — no reload, no popstate fire.
Zero deps, SSR-safe parsers
The two pure methods (parse, toQueryString) work in browser, Node, and edge workers. Browser-only methods are clearly demarcated.
Install
npm install @mongez/query-stringyarn add @mongez/query-stringpnpm add @mongez/query-stringZero runtime dependencies.
Quick peek
import queryString from "@mongez/query-string";
queryString.parse("?page=2&tags[]=a&tags[]=b&user[name]=alice");// → { page: 2, tags: ["a", "b"], user: { name: "alice" } }
queryString.toQueryString({ page: 2, tags: ["a", "b"] });// → "page=2&tags[]=a&tags[]=b"
queryString.update({ tag: "books", page: 1 });// window.location.search becomes "?tag=books&page=1" — no reloadParse and serialize with array + nested-object support, plus a no-reload URL rewriter for browser code.
Mental model
| Concept | Type | Mental model |
|---|---|---|
parse / toQueryString | pure function | Stateless conversion between Record<string, any> and string. Same in browser and server. |
all / get / toString | browser read | Sugar over parse(window.location.search). |
update | browser write | Sugar over history.replaceState. |
| Numeric coercion | parse-time | Values matching !isNaN(v - parseFloat(v)) become numbers. Lossy: "007" → 7. |
| Array shape | URL convention | key[]=a&key[]=b ⇄ { key: ["a", "b"] }. |
| Object shape | URL convention | parent[child]=v ⇄ { parent: { child: v } }. Any depth. |
Environment
- Browser: every method works.
- Server / Worker:
parseandtoQueryStringare safe.all/get/toString/updatereferencewindow.location/window.historyand will throw. Guard withtypeof window !== "undefined"if the same module loads on both sides.
Idioms
- Call
updatefrom event handlers, not effects. It’s a deliberate side effect on browser history; running it in auseEffectmakes the URL flip on every render. - Don’t trust
get(key, default)for falsy values. Usekey in queryString.all()if you need a presence check. - Pass raw strings to the serializer.
toQueryStringruns values throughencodeURIComponentfor you — pre-encoding double-encodes. - Use
update({})to clear the query string. It rewrites the URL to just the pathname. updatedoes not firepopstate. If you mirror URL state into another store, subscribe to your store, notpopstate.