query-string vs url-parse vs url-parse-lax vs url-search-params-polyfill
URL Parsing and Query String Manipulation Strategies
query-stringurl-parseurl-parse-laxurl-search-params-polyfillSimilar Packages:

URL Parsing and Query String Manipulation Strategies

These libraries handle different aspects of URL processing in JavaScript applications. query-string focuses strictly on parsing and stringifying the query portion of a URL (the part after ?). url-parse breaks down a complete URL into its components like protocol, host, and pathname. url-parse-lax is a wrapper around url-parse that tolerates missing protocols, making it useful for user-generated input. url-search-params-polyfill provides a fallback implementation of the native URLSearchParams API for older environments that lack support.

Npm Package Weekly Downloads Trend

3 Years

Github Stars Ranking

Stat Detail

Package
Downloads
Stars
Size
Issues
Publish
License
query-string06,90859.1 kB2a month agoMIT
url-parse01,03463 kB16-MIT
url-parse-lax0555.24 kB0a year agoMIT
url-search-params-polyfill060017.4 kB33 years agoMIT

Handling URLs and Query Parameters in Modern JavaScript

Working with URLs is a daily task in frontend development. Whether you are reading filters from the address bar, validating user input, or constructing API links, you need reliable tools. The packages query-string, url-parse, url-parse-lax, and url-search-params-polyfill each solve specific parts of this puzzle. Let's look at how they differ and where they fit in your stack.

🎯 Scope: Full URL vs. Query Only

The first decision is whether you need to parse the entire address or just the parameters.

query-string works only on the search part. It ignores the domain and protocol. You pass it the string that comes after the ?.

import queryString from 'query-string';

const params = queryString.parse('foo=1&bar=2');
// Result: { foo: '1', bar: '2' }

url-parse handles the complete address. It splits the protocol, host, port, and path into an object.

import UrlParse from 'url-parse';

const url = new UrlParse('https://example.com/path?foo=1');
// Result: { protocol: 'https:', hostname: 'example.com', query: '?foo=1' }

url-parse-lax also handles the complete address but is more forgiving. It accepts strings without a protocol.

import urlParseLax from 'url-parse-lax';

const url = urlParseLax('example.com/path?foo=1');
// Result: { protocol: 'https:', hostname: 'example.com', ... }

url-search-params-polyfill does not parse URLs itself. It adds the URLSearchParams class to the global scope so you can use the native API on old browsers.

import 'url-search-params-polyfill';

const params = new URLSearchParams('foo=1&bar=2');
// Result: URLSearchParams object with native methods

🛡️ Handling Incomplete User Input

Users often paste links without the http:// or https:// part. Standard parsers often fail or treat the domain as a path.

query-string does not handle this. It expects a clean query string. If you pass a full URL, it will likely return empty or incorrect results.

import queryString from 'query-string';

// Incorrect usage for full URLs
const params = queryString.parse('example.com?foo=1');
// Result: { 'example.com?foo': '1' } (Treats domain as key)

url-parse requires a valid protocol. Without it, the hostname might end up in the pathname field.

import UrlParse from 'url-parse';

const url = new UrlParse('example.com/path');
// Result: { hostname: '', pathname: 'example.com/path' }

url-parse-lax is built exactly for this case. It assumes https:// if no protocol is found.

import urlParseLax from 'url-parse-lax';

const url = urlParseLax('example.com/path');
// Result: { protocol: 'https:', hostname: 'example.com', pathname: '/path' }

url-search-params-polyfill relies on the URL constructor or string input for URLSearchParams. If the input is not a valid query string, it will not fix the missing protocol issue.

import 'url-search-params-polyfill';

// Fails if passed a full URL without extracting search first
const params = new URLSearchParams('example.com?foo=1');
// Result: Treats 'example.com' as a key

🔄 Stringification and Encoding

Turning data back into a URL string is where encoding rules matter. Special characters must be escaped correctly.

query-string gives you control over encoding. It handles arrays and objects gracefully by default.

import queryString from 'query-string';

const str = queryString.stringify({ q: 'hello world' });
// Result: 'q=hello+world'

url-parse does not stringify query objects directly. You usually update the .query property and call .toString().

import UrlParse from 'url-parse';

const url = new UrlParse('https://example.com');
url.query = { q: 'hello world' }; // This often requires manual stringifying
// Better: url.set('query', 'q=hello+world');

url-parse-lax inherits the behavior of url-parse. It is primarily for parsing, not building strings from scratch.

import urlParseLax from 'url-parse-lax';

// Primarily a parser, not a stringifier
const url = urlParseLax('example.com');
// To build: use .toString() after modification

url-search-params-polyfill enables the native .toString() method on the URLSearchParams instance.

import 'url-search-params-polyfill';

const params = new URLSearchParams();
params.append('q', 'hello world');
const str = params.toString();
// Result: 'q=hello+world'

📅 Maintenance and Native Support

Browser support changes how we choose tools. Modern browsers have built-in APIs that make some packages redundant.

query-string is actively maintained. It fills gaps where the native API is verbose or behaves differently regarding arrays and sorting.

// Native API requires more code for arrays
const params = new URLSearchParams();
params.append('tag', 'js');
params.append('tag', 'node');

url-parse is stable and widely used in Node.js and browser environments where a lightweight parser is needed without the full Node url module.

// Node native 'url' module is heavier in some bundlers
import UrlParse from 'url-parse'; // Lightweight alternative

url-parse-lax is useful but niche. If you control your input sources, you might not need the lax behavior.

// If input is trusted, standard url-parse is safer
import UrlParse from 'url-parse';

url-search-params-polyfill is largely obsolete. All modern evergreen browsers support URLSearchParams natively. Using this adds weight for no gain in 2024+.

// Modern approach: No import needed
const params = new URLSearchParams(window.location.search);

📊 Summary Table

Featurequery-stringurl-parseurl-parse-laxurl-search-params-polyfill
Input ScopeQuery string onlyFull URLFull URL (incomplete OK)Query string (via Global)
Missing Protocol❌ N/A❌ Fails✅ Auto-adds https❌ N/A
Stringify✅ Built-in⚠️ Via .toString()⚠️ Via .toString()✅ Native .toString()
Modern Need✅ High (DX)✅ Medium⚠️ Low (Specific)❌ None (Native)
Array Support✅ Excellent⚠️ Manual⚠️ Manual✅ Native

💡 Final Recommendation

For most modern frontend applications, start with the native URL and URLSearchParams APIs. They are built into the browser and require no dependencies. If you need better developer experience for handling arrays or encoding options, query-string is the best addition. It is lightweight and solves common pain points without reinventing the wheel.

Use url-parse if you are in an environment where the native URL object is unavailable or too heavy, such as older Node.js versions or specific serverless runtimes. Use url-parse-lax only if you are building a tool that accepts raw user input like a link shortener or a dashboard where users paste incomplete URLs.

Avoid url-search-params-polyfill in new projects. The cost of supporting Internet Explorer 11 is no longer justified for most teams. Rely on the native implementation and let your build tools handle transpilation if absolutely necessary. Choose tools that match your actual browser support matrix — do not carry legacy weight into modern codebases.

How to Choose: query-string vs url-parse vs url-parse-lax vs url-search-params-polyfill

  • query-string:

    Choose query-string when you only need to work with the query parameters of a URL and not the full address. It is ideal for scenarios like reading filters from a browser address bar or building search links where you already have the base path. It offers robust handling of arrays and encoding options that the native API sometimes handles differently.

  • url-parse:

    Choose url-parse when you need to deconstruct a complete URL into its parts, such as extracting the hostname or port from a full string. It is suitable for server-side rendering logic, proxy configurations, or validation tasks where the protocol and domain matter as much as the path.

  • url-parse-lax:

    Choose url-parse-lax when dealing with untrusted or incomplete user input that might lack a protocol scheme (like http://). It is best for input fields where users paste links without thinking about the scheme, ensuring your parser does not crash on valid but incomplete URLs.

  • url-search-params-polyfill:

    Choose url-search-params-polyfill only if you must support legacy browsers like Internet Explorer 11 that do not have the native URLSearchParams API. For all modern projects targeting evergreen browsers, this package is unnecessary and should be avoided in favor of the built-in web standard.

README for query-string

query-string

Parse and stringify URL query strings

Install

npm install query-string

[!WARNING] Remember the hyphen! Do not install the deprecated querystring package!

For browser usage, this package targets the latest version of Chrome, Firefox, and Safari.

[!TIP] Consider using URLSearchParams for simple use cases. It's a native browser API that handles basic query string operations.

Usage

import queryString from 'query-string';

console.log(location.search);
//=> '?foo=bar'

const parsed = queryString.parse(location.search);
console.log(parsed);
//=> {foo: 'bar'}

console.log(location.hash);
//=> '#token=bada55cafe'

const parsedHash = queryString.parse(location.hash);
console.log(parsedHash);
//=> {token: 'bada55cafe'}

parsed.foo = 'unicorn';
parsed.ilike = 'pizza';

const stringified = queryString.stringify(parsed);
//=> 'foo=unicorn&ilike=pizza'

location.search = stringified;
// note that `location.search` automatically prepends a question mark
console.log(location.search);
//=> '?foo=unicorn&ilike=pizza'

API

.parse(string, options?)

Parse a query string into an object. Leading ? or # are ignored, so you can pass location.search or location.hash directly.

The returned object is created with Object.create(null) and thus does not have a prototype.

queryString.parse('?foo=bar');
//=> {foo: 'bar'}

queryString.parse('#token=secret&name=jhon');
//=> {token: 'secret', name: 'jhon'}

options

Type: object

decode

Type: boolean
Default: true

Decode the keys and values. URL components are decoded with decode-uri-component.

arrayFormat

Type: string
Default: 'none'

  • 'bracket': Parse arrays with bracket representation:
import queryString from 'query-string';

queryString.parse('foo[]=1&foo[]=2&foo[]=3', {arrayFormat: 'bracket'});
//=> {foo: ['1', '2', '3']}
  • 'index': Parse arrays with index representation:
import queryString from 'query-string';

queryString.parse('foo[0]=1&foo[1]=2&foo[3]=3', {arrayFormat: 'index'});
//=> {foo: ['1', '2', '3']}
  • 'comma': Parse arrays with elements separated by comma:
import queryString from 'query-string';

queryString.parse('foo=1,2,3', {arrayFormat: 'comma'});
//=> {foo: ['1', '2', '3']}
  • 'separator': Parse arrays with elements separated by a custom character:
import queryString from 'query-string';

queryString.parse('foo=1|2|3', {arrayFormat: 'separator', arrayFormatSeparator: '|'});
//=> {foo: ['1', '2', '3']}
  • 'bracket-separator': Parse arrays (that are explicitly marked with brackets) with elements separated by a custom character:
import queryString from 'query-string';

queryString.parse('foo[]', {arrayFormat: 'bracket-separator', arrayFormatSeparator: '|'});
//=> {foo: []}

queryString.parse('foo[]=', {arrayFormat: 'bracket-separator', arrayFormatSeparator: '|'});
//=> {foo: ['']}

queryString.parse('foo[]=1', {arrayFormat: 'bracket-separator', arrayFormatSeparator: '|'});
//=> {foo: ['1']}

queryString.parse('foo[]=1|2|3', {arrayFormat: 'bracket-separator', arrayFormatSeparator: '|'});
//=> {foo: ['1', '2', '3']}

queryString.parse('foo[]=1||3|||6', {arrayFormat: 'bracket-separator', arrayFormatSeparator: '|'});
//=> {foo: ['1', '', 3, '', '', '6']}

queryString.parse('foo[]=1|2|3&bar=fluffy&baz[]=4', {arrayFormat: 'bracket-separator', arrayFormatSeparator: '|'});
//=> {foo: ['1', '2', '3'], bar: 'fluffy', baz:['4']}
  • 'colon-list-separator': Parse arrays with parameter names that are explicitly marked with :list:
import queryString from 'query-string';

queryString.parse('foo:list=one&foo:list=two', {arrayFormat: 'colon-list-separator'});
//=> {foo: ['one', 'two']}
  • 'none': Parse arrays with elements using duplicate keys:
import queryString from 'query-string';

queryString.parse('foo=1&foo=2&foo=3');
//=> {foo: ['1', '2', '3']}
arrayFormatSeparator

Type: string
Default: ','

The character used to separate array elements when using {arrayFormat: 'separator'}.

sort

Type: Function | boolean
Default: true

Supports both Function as a custom sorting function or false to disable sorting.

parseNumbers

Type: boolean
Default: false

import queryString from 'query-string';

queryString.parse('foo=1', {parseNumbers: true});
//=> {foo: 1}

Parse the value as a number type instead of string type if it's a number.

parseBooleans

Type: boolean
Default: false

import queryString from 'query-string';

queryString.parse('foo=true', {parseBooleans: true});
//=> {foo: true}

Parse the value as a boolean type instead of string type if it's a boolean.

types

Type: object
Default: {}

Specifies a schema for parsing query values with explicit type declarations. When defined, the types provided here take precedence over general parsing options such as parseNumbers, parseBooleans, and arrayFormat.

Use this option to explicitly define the type of a specific parameter—particularly useful in cases where the type might otherwise be ambiguous (e.g., phone numbers or IDs).

You can also provide a custom function to transform the value. The function will receive the raw string and should return the desired parsed result. When used with array formats (like comma, separator, bracket, etc.), the function is applied to each array element individually.

Supported Types:

  • 'boolean': Parse flagged as a boolean (overriding the parseBooleans option):
queryString.parse('?isAdmin=true&flagged=true&isOkay=0', {
		parseBooleans: false,
		types: {
				flagged: 'boolean',
				isOkay: 'boolean',
		},
});
//=> {isAdmin: 'true', flagged: true, isOkay: false}

Note: The 'boolean' type also converts '0' and '1' to booleans, and treats valueless keys (e.g. ?flag) as true.

  • 'string': Parse phoneNumber as a string (overriding the parseNumbers option):
import queryString from 'query-string';

queryString.parse('?phoneNumber=%2B380951234567&id=1', {
	parseNumbers: true,
	types: {
		phoneNumber: 'string',
	}
});
//=> {phoneNumber: '+380951234567', id: 1}
  • 'number': Parse age as a number (even when parseNumbers is false):
import queryString from 'query-string';

queryString.parse('?age=20&id=01234&zipcode=90210', {
	types: {
		age: 'number',
	}
});
//=> {age: 20, id: '01234', zipcode: '90210'}
  • 'string[]': Parse items as an array of strings (overriding the parseNumbers option):
import queryString from 'query-string';

queryString.parse('?age=20&items=1%2C2%2C3', {
	parseNumbers: true,
	types: {
		items: 'string[]',
	}
});
//=> {age: 20, items: ['1', '2', '3']}
  • 'number[]': Parse items as an array of numbers (even when parseNumbers is false):
import queryString from 'query-string';

queryString.parse('?age=20&items=1%2C2%2C3', {
	types: {
		items: 'number[]',
	}
});
//=> {age: '20', items: [1, 2, 3]}
  • 'Function': Provide a custom function as the parameter type. The parameter's value will equal the function's return value. When used with array formats (like comma, separator, bracket, etc.), the function is applied to each array element individually.
import queryString from 'query-string';

queryString.parse('?age=20&id=01234&zipcode=90210', {
	types: {
		age: value => value * 2,
	}
});
//=> {age: 40, id: '01234', zipcode: '90210'}

// With arrays, the function is applied to each element
queryString.parse('?scores=10,20,30', {
	arrayFormat: 'comma',
	types: {
		scores: value => Number(value) * 2,
	}
});
//=> {scores: [20, 40, 60]}

NOTE: Array types (string[], number[]) are ignored if arrayFormat is set to 'none'.

queryString.parse('ids=001%2C002%2C003&foods=apple%2Corange%2Cmango', {
	arrayFormat: 'none',
	types: {
		ids: 'number[]',
		foods: 'string[]',
	},
}
//=> {ids:'001,002,003', foods:'apple,orange,mango'}
Function
import queryString from 'query-string';

queryString.parse('?age=20&id=01234&zipcode=90210', {
	types: {
		age: value => value * 2,
	}
});
//=> {age: 40, id: '01234', zipcode: '90210'}

Parse the value as a boolean type instead of string type if it's a boolean.

.stringify(object, options?)

Stringify an object into a query string and sorting the keys.

Supported value types: string, number, bigint, boolean, null, undefined, and arrays of these types. Other types like Symbol, functions, or objects (except arrays) will throw an error.

options

Type: object

strict

Type: boolean
Default: true

Strictly encode URI components. It uses encodeURIComponent if set to false. You probably don't care about this option.

encode

Type: boolean
Default: true

URL encode the keys and values.

arrayFormat

Type: string
Default: 'none'

  • 'bracket': Serialize arrays using bracket representation:
import queryString from 'query-string';

queryString.stringify({foo: [1, 2, 3]}, {arrayFormat: 'bracket'});
//=> 'foo[]=1&foo[]=2&foo[]=3'
  • 'index': Serialize arrays using index representation:
import queryString from 'query-string';

queryString.stringify({foo: [1, 2, 3]}, {arrayFormat: 'index'});
//=> 'foo[0]=1&foo[1]=2&foo[2]=3'
  • 'comma': Serialize arrays by separating elements with comma:
import queryString from 'query-string';

queryString.stringify({foo: [1, 2, 3]}, {arrayFormat: 'comma'});
//=> 'foo=1,2,3'

queryString.stringify({foo: [1, null, '']}, {arrayFormat: 'comma'});
//=> 'foo=1,,'
// Note that typing information for null values is lost
// and `.parse('foo=1,,')` would return `{foo: [1, '', '']}`.
  • 'separator': Serialize arrays by separating elements with a custom character:
import queryString from 'query-string';

queryString.stringify({foo: [1, 2, 3]}, {arrayFormat: 'separator', arrayFormatSeparator: '|'});
//=> 'foo=1|2|3'
  • 'bracket-separator': Serialize arrays by explicitly post-fixing array names with brackets and separating elements with a custom character:
import queryString from 'query-string';

queryString.stringify({foo: []}, {arrayFormat: 'bracket-separator', arrayFormatSeparator: '|'});
//=> 'foo[]'

queryString.stringify({foo: ['']}, {arrayFormat: 'bracket-separator', arrayFormatSeparator: '|'});
//=> 'foo[]='

queryString.stringify({foo: [1]}, {arrayFormat: 'bracket-separator', arrayFormatSeparator: '|'});
//=> 'foo[]=1'

queryString.stringify({foo: [1, 2, 3]}, {arrayFormat: 'bracket-separator', arrayFormatSeparator: '|'});
//=> 'foo[]=1|2|3'

queryString.stringify({foo: [1, '', 3, null, null, 6]}, {arrayFormat: 'bracket-separator', arrayFormatSeparator: '|'});
//=> 'foo[]=1||3|||6'

queryString.stringify({foo: [1, '', 3, null, null, 6]}, {arrayFormat: 'bracket-separator', arrayFormatSeparator: '|', skipNull: true});
//=> 'foo[]=1||3|6'

queryString.stringify({foo: [1, 2, 3], bar: 'fluffy', baz: [4]}, {arrayFormat: 'bracket-separator', arrayFormatSeparator: '|'});
//=> 'foo[]=1|2|3&bar=fluffy&baz[]=4'
  • 'colon-list-separator': Serialize arrays with parameter names that are explicitly marked with :list:
import queryString from 'query-string';

queryString.stringify({foo: ['one', 'two']}, {arrayFormat: 'colon-list-separator'});
//=> 'foo:list=one&foo:list=two'
  • 'none': Serialize arrays by using duplicate keys:
import queryString from 'query-string';

queryString.stringify({foo: [1, 2, 3]});
//=> 'foo=1&foo=2&foo=3'
arrayFormatSeparator

Type: string
Default: ','

The character used to separate array elements when using {arrayFormat: 'separator'}.

sort

Type: Function | boolean

Supports both Function as a custom sorting function or false to disable sorting.

import queryString from 'query-string';

const order = ['c', 'a', 'b'];

queryString.stringify({a: 1, b: 2, c: 3}, {
	sort: (a, b) => order.indexOf(a) - order.indexOf(b)
});
//=> 'c=3&a=1&b=2'
import queryString from 'query-string';

queryString.stringify({b: 1, c: 2, a: 3}, {sort: false});
//=> 'b=1&c=2&a=3'

If omitted, keys are sorted using Array#sort(), which means, converting them to strings and comparing strings in Unicode code point order.

skipNull

Skip keys with null as the value.

Note that keys with undefined as the value are always skipped.

Type: boolean
Default: false

import queryString from 'query-string';

queryString.stringify({a: 1, b: undefined, c: null, d: 4}, {
	skipNull: true
});
//=> 'a=1&d=4'
import queryString from 'query-string';

queryString.stringify({a: undefined, b: null}, {
	skipNull: true
});
//=> ''
skipEmptyString

Skip keys with an empty string as the value.

Type: boolean
Default: false

import queryString from 'query-string';

queryString.stringify({a: 1, b: '', c: '', d: 4}, {
	skipEmptyString: true
});
//=> 'a=1&d=4'
import queryString from 'query-string';

queryString.stringify({a: '', b: ''}, {
	skipEmptyString: true
});
//=> ''
replacer

A function that transforms key-value pairs before stringification.

Type: function
Default: undefined

Similar to the replacer parameter of JSON.stringify(), this function is called for each key-value pair and can be used to transform values before they are stringified. The function receives the key and value, and should return the transformed value. Returning undefined will omit the key-value pair from the resulting query string.

This is useful for custom serialization of non-primitive types like Date:

import queryString from 'query-string';

queryString.stringify({
	date: new Date('2024-01-15T10:30:00Z'),
	name: 'John'
}, {
	replacer: (key, value) => {
		if (value instanceof Date) {
			return value.toISOString();
		}

		return value;
	}
});
//=> 'date=2024-01-15T10%3A30%3A00.000Z&name=John'

You can also use it to filter out keys:

import queryString from 'query-string';

queryString.stringify({
	a: 1,
	b: null,
	c: 3
}, {
	replacer: (key, value) => value === null ? undefined : value
});
//=> 'a=1&c=3'

.extract(string)

Extract a query string from a URL that can be passed into .parse().

queryString.extract('https://foo.bar?foo=bar');
//=> 'foo=bar'

.parseUrl(string, options?)

Extract the URL and the query string as an object.

Returns an object with a url and query property.

If the parseFragmentIdentifier option is true, the object will also contain a fragmentIdentifier property.

import queryString from 'query-string';

queryString.parseUrl('https://foo.bar?foo=bar');
//=> {url: 'https://foo.bar', query: {foo: 'bar'}}

queryString.parseUrl('https://foo.bar?foo=bar#xyz', {parseFragmentIdentifier: true});
//=> {url: 'https://foo.bar', query: {foo: 'bar'}, fragmentIdentifier: 'xyz'}

options

Type: object

The options are the same as for .parse().

Extra options are as below.

parseFragmentIdentifier

Parse the fragment identifier from the URL.

Type: boolean
Default: false

import queryString from 'query-string';

queryString.parseUrl('https://foo.bar?foo=bar#xyz', {parseFragmentIdentifier: true});
//=> {url: 'https://foo.bar', query: {foo: 'bar'}, fragmentIdentifier: 'xyz'}

.stringifyUrl(object, options?)

Stringify an object into a URL with a query string and sorting the keys. The inverse of .parseUrl()

The options are the same as for .stringify().

Returns a string with the URL and a query string.

Query items in the query property overrides queries in the url property.

The fragmentIdentifier property overrides the fragment identifier in the url property.

queryString.stringifyUrl({url: 'https://foo.bar', query: {foo: 'bar'}});
//=> 'https://foo.bar?foo=bar'

queryString.stringifyUrl({url: 'https://foo.bar?foo=baz', query: {foo: 'bar'}});
//=> 'https://foo.bar?foo=bar'

queryString.stringifyUrl({
	url: 'https://foo.bar',
	query: {
		top: 'foo'
	},
	fragmentIdentifier: 'bar'
});
//=> 'https://foo.bar?top=foo#bar'

object

Type: object

url

Type: string

The URL to stringify.

query

Type: object

Query items to add to the URL.

.pick(url, keys, options?)

.pick(url, filter, options?)

Pick query parameters from a URL.

Returns a string with the new URL.

import queryString from 'query-string';

queryString.pick('https://foo.bar?foo=1&bar=2#hello', ['foo']);
//=> 'https://foo.bar?foo=1#hello'

queryString.pick('https://foo.bar?foo=1&bar=2#hello', (name, value) => value === 2, {parseNumbers: true});
//=> 'https://foo.bar?bar=2#hello'

.exclude(url, keys, options?)

.exclude(url, filter, options?)

Exclude query parameters from a URL.

Returns a string with the new URL.

import queryString from 'query-string';

queryString.exclude('https://foo.bar?foo=1&bar=2#hello', ['foo']);
//=> 'https://foo.bar?bar=2#hello'

queryString.exclude('https://foo.bar?foo=1&bar=2#hello', (name, value) => value === 2, {parseNumbers: true});
//=> 'https://foo.bar?foo=1#hello'

url

Type: string

The URL containing the query parameters to filter.

keys

Type: string[]

The names of the query parameters to filter based on the function used.

filter

Type: (key, value) => boolean

A filter predicate that will be provided the name of each query parameter and its value. The parseNumbers and parseBooleans options also affect value.

options

Type: object

Parse options and stringify options.

Nesting

This module intentionally doesn't support nesting as it's not spec'd and varies between implementations, which causes a lot of edge cases.

You're much better off just converting the object to a JSON string:

import queryString from 'query-string';

queryString.stringify({
	foo: 'bar',
	nested: JSON.stringify({
		unicorn: 'cake'
	})
});
//=> 'foo=bar&nested=%7B%22unicorn%22%3A%22cake%22%7D'

However, there is support for multiple instances of the same key:

import queryString from 'query-string';

queryString.parse('likes=cake&name=bob&likes=icecream');
//=> {likes: ['cake', 'icecream'], name: 'bob'}

queryString.stringify({color: ['taupe', 'chartreuse'], id: '515'});
//=> 'color=taupe&color=chartreuse&id=515'

Falsy values

Sometimes you want to unset a key, or maybe just make it present without assigning a value to it. Here is how falsy values are stringified:

import queryString from 'query-string';

queryString.stringify({foo: false});
//=> 'foo=false'

queryString.stringify({foo: null});
//=> 'foo'

queryString.stringify({foo: undefined});
//=> ''

FAQ

Why is it parsing + as a space?

See this answer.