A query string is the part of a URL after the ?. It carries extra information for the page or the server, such as a search term, a filter, or a page number, as key=value pairs joined by &. It runs to the end of the URL, or to a #, which starts the fragment, the part that points to a place within the page. Everything in it is text: page=2 carries the string "2". Here's a shop's search URL, https://shop.example/search?q=fish+%26+chips&tag=sale&tag=new&page=2, taken apart:
| Pair | Key | Value |
|---|---|---|
q=fish+%26+chips | q | fish & chips. Each + stands for a space, and %26 is an & that belongs to the value. |
tag=sale | tag | sale |
tag=new | tag | new. A key can appear more than once; that's a common way to send a list. |
page=2 | page | 2, as text |
The value fish & chips couldn't go in as it is: its & would be read as the start of a new pair, and a URL can't contain spaces. A # in a value would end the query too. Percent-encoding writes such a character as % followed by two hexadecimal digits, the value of its byte: a space is byte 32, or 20 in hex, so it becomes %20. A character outside ASCII (the basic English letters, digits, and symbols) is first turned into its bytes in UTF-8, the standard text encoding, so é, which takes two bytes, becomes %C3%A9. A % itself becomes %25. JavaScript's encodeURIComponent encodes one key or value this way, and decodeURIComponent reverses it:
encodeURIComponent("fish & chips"); // "fish%20%26%20chips"
encodeURIComponent("1+1=2"); // "1%2B1%3D2"
encodeURIComponent("café"); // "caf%C3%A9"
decodeURIComponent("caf%C3%A9"); // "café"
decodeURIComponent("fish+%26+chips"); // "fish+&+chips": it leaves + alone
Spaces have a second spelling. When an HTML form sends its fields in a query string, it writes each space as +, and most code that reads query strings, URLSearchParams included, reads every + as a space. So a real plus sign has to travel as %2B. encodeURIComponent writes spaces as %20, which every reader understands.
You meet query strings in search pages, filters, pagination, and tracking links, and you add them to fetch requests. Browsers and Node.js have URLSearchParams, also available as url.searchParams on a URL object, which reads and writes them for you.
Write toQueryString(params). It turns an object into a URL query string: key=value pairs joined with &, in the object's key order, with no leading ?.
Encode every key and value with encodeURIComponent, so a space becomes %20 (not +), and &, =, ?, #, /, and non-ASCII characters are percent-encoded as well.
Values are strings, numbers, booleans, null, undefined, or arrays of those. An array adds the key once per item, in order. Skip null and undefined, both as values and as array items, but keep 0, false, and "" (which gives key=). When there's nothing to add, return "".
toQueryString({ q: "red shoes", page: 2, tags: ["a", "b"], sort: undefined });
// "q=red%20shoes&page=2&tags=a&tags=b"
expect(toQueryString({ a: "1", b: 2, c: true })).toBe("a=1&b=2&c=true");
expect(toQueryString({ q: "red shoes & socks", "a b": "x=y/z?#" })).toBe("q=red%20shoes%20%26%20socks&a%20b=x%3Dy%2Fz%3F%23");
expect(toQueryString({ name: "José", city: "東京" })).toBe("name=Jos%C3%A9&city=%E6%9D%B1%E4%BA%AC");
expect(toQueryString({ tags: ["a", "b c"], page: 1 })).toBe("tags=a&tags=b%20c&page=1");
expect(toQueryString({ none: [], x: 1 })).toBe("x=1");
expect(toQueryString({ a: null, b: undefined, c: "x", d: [1, null, 2, undefined] })).toBe("c=x&d=1&d=2");
expect(toQueryString({ zero: 0, no: false, empty: "" })).toBe("zero=0&no=false&empty=");
expect(toQueryString({})).toBe("");
expect(toQueryString({ a: null, b: [] })).toBe("");
Run tests runs every test on your code, here in your browser. The problem is solved when they all pass.
Ctrl + Enter runs the tests (⌘ + Enter on a Mac).
I'm here to help with Build a query string. I give hints, never the solution, so you get to solve it yourself. Ask for a hint, select code to ask about it, or press Ask Tuto why on a failing test.
Your first 3 answers need no account. for free to keep asking.