Skip to main content

Cloudsoft Zone Build, deploy and scale with practical guides to PHP, Node.js, React, DevOps and AI automation. Explore the guides

Cloudsoft Zone
JavaScriptDevelopmentTutorial

How to convert a JavaScript object to a query string (and back)

Turn a JavaScript object into URL query parameters with URLSearchParams, handle arrays, null, dates and nested objects correctly, and parse a query string back into an object.

Cloudsoft Zone

Updated 4 min read

A laptop screen showing JavaScript code in a text editor, with the Cloudsoft Zone logo in the corner
In this article

The quickest way to convert a JavaScript object to a query string is new URLSearchParams(object).toString(). It is built into every browser and Node.js and encodes values correctly. It does have gaps: null and undefined become the text "null" and "undefined", arrays become comma-joined text, and nested objects become [object Object]. This guide shows the one-liner, a small tested helper that fixes those gaps, a version for nested objects, and how to read a query string back into an object.

Tested with Node.js 24.19.0 (V8 13.6). Every result shown in a comment was printed by running the code; the same code runs in Deno, Bun and current Chrome, Firefox and Safari.

The one-liner: URLSearchParams

JavaScript
const params = new URLSearchParams({ q: 'red shoes', page: 2, sort: 'price&asc' });
console.log(params.toString()); // q=red+shoes&page=2&sort=price%26asc

const url = new URL('https://shop.example/search');
url.search = params;
console.log(url.href); // https://shop.example/search?q=red+shoes&page=2&sort=price%26asc

// Values are converted with String(), which is rarely what you want for these:
console.log(new URLSearchParams({ a: null, b: undefined, c: [1, 2], d: { x: 1 } }).toString());
// a=null&b=undefined&c=1%2C2&d=%5Bobject+Object%5D

Two details in that output matter. URLSearchParams encodes a space as +, which servers read as a space in a query string, and it encodes & and = inside values so they cannot break the string apart. Assigning it to url.search on a URL object is the safest way to build a full address, because the base URL is parsed and validated too.

A helper for arrays, null, undefined and dates

Most real objects, such as a form's filter state, contain empty values and lists. This helper leaves out null and undefined, repeats the key for each array item (the format most servers expect) and sends dates as ISO 8601. It refuses nested objects rather than sending [object Object]:

JavaScript
// Skips null and undefined, repeats the key for arrays (tag=a&tag=b), and turns dates into ISO strings.
export function toQueryString(values) {
  const params = new URLSearchParams();
  for (const [key, value] of Object.entries(values)) {
    for (const item of Array.isArray(value) ? value : [value]) {
      if (item === null || item === undefined) continue;
      if (typeof item === 'object' && !(item instanceof Date)) throw new TypeError(`Nested value for "${key}": flatten it first`);
      params.append(key, item instanceof Date ? item.toISOString() : String(item));
    }
  }
  return params.toString();
}
JavaScript
toQueryString({
  q: 'café au lait', tag: ['php', 'js'], page: 1, draft: false, author: null,
  from: new Date('2026-10-01T00:00:00Z'),
});
// q=caf%C3%A9+au+lait&tag=php&tag=js&page=1&draft=false&from=2026-10-01T00%3A00%3A00.000Z

false and 0 are kept, because they are real values; only missing values are dropped. If your API expects tag=php,js or tag[]=php instead of a repeated key, change the inner loop; check the server's documentation, because there is no single standard for arrays in query strings.

Nested objects: bracket notation

PHP, Ruby on Rails and the popular qs package read filter[price][max]=50 as a nested object. To send that format, recurse and build the bracketed names:

JavaScript
// Bracket notation (filter[price][max]=50), the format PHP, Rails and the qs package read as nested objects.
export function toBracketQuery(values, prefix = '', params = new URLSearchParams()) {
  for (const [key, value] of Object.entries(values)) {
    const name = prefix ? `${prefix}[${key}]` : key;
    if (value === null || value === undefined) continue;
    if (Array.isArray(value)) value.forEach((item) => params.append(`${name}[]`, String(item)));
    else if (typeof value === 'object' && !(value instanceof Date)) toBracketQuery(value, name, params);
    else params.append(name, value instanceof Date ? value.toISOString() : String(value));
  }
  return params;
}
JavaScript
toBracketQuery({ filter: { price: { max: 50 }, brand: ['acme', 'zen'] }, q: 'shoes' }).toString();
// filter%5Bprice%5D%5Bmax%5D=50&filter%5Bbrand%5D%5B%5D=acme&filter%5Bbrand%5D%5B%5D=zen&q=shoes
// decoded: filter[price][max]=50&filter[brand][]=acme&filter[brand][]=zen&q=shoes

We checked that PHP's parse_str reads that string back as {"filter":{"price":{"max":"50"},"brand":["acme","zen"]}}. For deeply nested or frequently changing structures, sending JSON in a POST body is usually simpler than a long query string.

Parsing a query string back into an object

JavaScript
const params = new URLSearchParams('tag=php&tag=js&page=2&q=red+shoes');
params.get('q');        // 'red shoes'
params.get('page');     // '2' (always a string: convert it yourself)
params.getAll('tag');   // [ 'php', 'js' ]
Object.fromEntries(params); // { tag: 'js', page: '2', q: 'red shoes' }  (the first tag is lost!)

// In the browser, for the current page:
const current = new URLSearchParams(window.location.search);

Object.fromEntries keeps only the last value for a repeated key, so use getAll() for any parameter that can repeat. Every value comes back as a string, so Number(params.get('page')) and similar conversions, with validation, are your job.

encodeURIComponent, querystring and jQuery

JavaScript
encodeURIComponent('red shoes & socks');            // 'red%20shoes%20%26%20socks'
new URLSearchParams({ x: 'red shoes & socks' }).toString(); // 'x=red+shoes+%26+socks'

const manual = (obj) => Object.entries(obj)
  .map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`).join('&');
manual({ q: 'red shoes', sort: 'price&asc' });  // 'q=red%20shoes&sort=price%26asc'
  • encodeURIComponent encodes a space as %20. Both forms are valid in a query string; use it when you build one value at a time, never encodeURI, which leaves & and = unencoded.
  • Node's querystring module still works (querystring.stringify({ q: 'red shoes', tag: ['a', 'b'] }) gives q=red%20shoes&tag=a&tag=b), but the Node.js documentation marks it as legacy. Use URLSearchParams, which is global in Node.js.
  • jQuery's $.param() produces bracket notation for nested data. There is no reason to load jQuery for this in new code.

Security notes

Query strings end up in server logs, browser history and the Referer header, so never put passwords, tokens or personal data in them. Treat parsed values as untrusted input: validate them on the server, and never insert them into HTML without escaping. To cancel a request built from these parameters when the user changes a filter, see cancelling fetch requests with AbortController.

Frequently asked questions

  • How do I convert an object to a query string in JavaScript?

    Use new URLSearchParams(object).toString(). For objects with null values, arrays or dates, build the parameters with append() in a loop so that empty values are skipped and arrays become repeated keys.

  • Why does URLSearchParams encode spaces as plus signs?

    It follows the application/x-www-form-urlencoded format, in which + means a space. Servers decode it as a space in query strings. encodeURIComponent uses %20; both are valid.

  • How do I add an array to a query string?

    Call params.append('tag', value) once per item to get tag=a&tag=b, and read it back with params.getAll('tag'). Some servers expect tag[]=a or tag=a,b instead, so check what yours reads.

  • Can URLSearchParams handle nested objects?

    No. Nested values are converted to the text [object Object]. Build bracket-notation names such as filter[price][max] yourself, or send nested data as JSON in a request body.

Advertise on CloudSoftZone

Your next customer could start right here.

Introduce your tool, product or business to readers exploring technology.

Advertise with us

Explore placements and request a private quote

Sources

  1. URLSearchParams — MDN Web Docs
  2. URL: search property — MDN Web Docs
  3. encodeURIComponent() — MDN Web Docs
  4. URL Standard: application/x-www-form-urlencoded — WHATWG
  5. Query string (legacy module) — Node.js
  6. parse_str — PHP Manual

Written with substantial AI assistance. Facts checked against cited sources. Editorial policy.

Comments

No comments yet. Be the first to join the conversation.

Leave a comment

Every comment is read by a moderator before it appears. Your email address is never published; it is used only to contact you about this comment.

Never published. Used only to contact you about this comment.

Loading the security check…

Comments are moderated before publication.