URL parameters are everywhere: search pages, product filters, campaign links, pagination, language selection, and shareable dashboards. If you are building a JavaScript app, you often need to read values from a query string like ?q=phone&page=2, update one value without breaking the rest of the URL, or generate a clean link that users can share.
This JavaScript URLSearchParams tutorial shows how to work with query parameters using the standard URL and URLSearchParams APIs. You will learn how to read, add, update, delete, and serialize parameters safely, with practical examples that run in modern browsers and current Node.js versions.
Key takeaways
- Use
new URL(urlString)when you have a complete URL and want reliable parsing. - Use
url.searchParamsto read and change query parameters attached to that URL. - Use
get(),getAll(),has(),set(),append(), anddelete()for common operations. - Do not manually concatenate query strings when user input or special characters are involved.
- Avoid storing secrets, tokens, or sensitive personal data in URL parameters because URLs can be logged, copied, cached, and shared.
What are URL parameters?
URL parameters, also called query parameters or query string parameters, are key-value pairs after the ? in a URL.
https://shop.example.in/products?category=books&page=2&sort=price
In this URL:
| Part | Meaning |
|---|---|
https://shop.example.in/products | Base URL |
? | Starts the query string |
category=books | Parameter named category with value books |
& | Separates parameters |
page=2 | Parameter named page with value 2 |
The browser does not automatically treat these values as numbers, booleans, arrays, or objects. Query parameter values are strings unless your code converts them.
Advertisement
Why use URLSearchParams instead of string splitting?
You may see code like this in older tutorials:
const query = window.location.search.substring(1);
const parts = query.split('&');
That approach works only for very simple URLs. It becomes error-prone when values contain spaces, &, =, Unicode characters, or repeated parameter names.
URLSearchParams is a built-in Web API designed for query strings. According to MDN, it provides utility methods to work with the query string of a URL. It handles encoding and decoding rules for you, which makes your code shorter and less fragile.
For a quick manual check while learning, you can also use TidyURLs to edit query parameters and compare the resulting URL with your JavaScript output.
Reading URL parameters in JavaScript
Read parameters from a full URL
Use URL when you have a complete URL string.
const url = new URL('https://shop.example.in/products?category=books&page=2&sort=price');
console.log(url.searchParams.get('category')); // 'books'
console.log(url.searchParams.get('page')); // '2'
console.log(url.searchParams.get('sort')); // 'price'
Remember that get() returns a string or null.
const page = url.searchParams.get('page');
const missing = url.searchParams.get('coupon');
console.log(typeof page); // 'string'
console.log(missing); // null
If you need a number, convert it explicitly:
const pageParam = url.searchParams.get('page');
const page = pageParam === null ? 1 : Number.parseInt(pageParam, 10);
console.log(page); // 2
Read parameters from the current browser URL
In a browser, window.location.href gives you the current page URL.
const currentUrl = new URL(window.location.href);
const searchQuery = currentUrl.searchParams.get('q');
console.log(searchQuery);
For example, if the current page is:
https://example.in/search?q=javascript&page=1
Then searchQuery will be 'javascript'.
Check whether a parameter exists
Use has() when you care whether the parameter is present, even if its value is empty.
const url = new URL('https://example.in/offers?coupon=');
console.log(url.searchParams.has('coupon')); // true
console.log(url.searchParams.get('coupon')); // ''
console.log(url.searchParams.has('utm_source')); // false
This distinction matters because ?coupon= and no coupon parameter are not the same thing.
Working with repeated parameters
URLs can contain the same parameter name more than once:
https://example.in/products?tag=mobile&tag=android&tag=budget
Use get() for the first value and getAll() for every value.
const url = new URL('https://example.in/products?tag=mobile&tag=android&tag=budget');
console.log(url.searchParams.get('tag')); // 'mobile'
console.log(url.searchParams.getAll('tag')); // ['mobile', 'android', 'budget']
Repeated parameters are common for filters, tags, selected categories, and multi-select form controls.
Modifying URL parameters
Add or update a parameter with set()
set() updates the parameter if it already exists. If it does not exist, set() adds it.
const url = new URL('https://shop.example.in/products?category=books&page=2');
url.searchParams.set('page', '3');
url.searchParams.set('sort', 'price-low-to-high');
console.log(url.toString());
// 'https://shop.example.in/products?category=books&page=3&sort=price-low-to-high'
If the parameter appears multiple times, set() replaces all existing values with one value.
const url = new URL('https://example.in/products?tag=mobile&tag=android');
url.searchParams.set('tag', 'laptop');
console.log(url.toString());
// 'https://example.in/products?tag=laptop'
Add another value with append()
Use append() when repeated values are intentional.
const url = new URL('https://example.in/products?tag=mobile');
url.searchParams.append('tag', 'android');
url.searchParams.append('tag', 'budget');
console.log(url.toString());
// 'https://example.in/products?tag=mobile&tag=android&tag=budget'
Remove a parameter with delete()
Use delete() to remove a parameter.
const url = new URL('https://example.in/products?category=books&utm_source=newsletter&page=2');
url.searchParams.delete('utm_source');
console.log(url.toString());
// 'https://example.in/products?category=books&page=2'
This is useful when cleaning tracking parameters, resetting filters, or removing temporary state from shareable links.
Updating the browser address bar without reloading
If you modify a URL object, the browser address bar does not change automatically. To update the visible URL without reloading the page, use the History API.
Use replaceState() when the change should not create a new Back button entry, such as updating a filter while typing.
const url = new URL(window.location.href);
url.searchParams.set('q', 'javascript');
url.searchParams.set('page', '1');
window.history.replaceState({}, '', url);
Use pushState() when the change represents a meaningful navigation step that the user may expect to return to with the Back button.
const url = new URL(window.location.href);
url.searchParams.set('category', 'laptops');
window.history.pushState({}, '', url);
These examples update the URL displayed in the browser. They do not automatically fetch new data or re-render your page. Your application code still needs to respond to the new state.
Advertisement
Practical workflow: build a product filter URL
Here is a small function that builds a product listing URL from filter values. It demonstrates common real-world needs: setting a category, resetting pagination, supporting multiple brands, and skipping empty values.
function buildProductUrl(baseUrl, filters) {
const url = new URL(baseUrl);
if (filters.category) {
url.searchParams.set('category', filters.category);
} else {
url.searchParams.delete('category');
}
if (filters.sort) {
url.searchParams.set('sort', filters.sort);
} else {
url.searchParams.delete('sort');
}
// Reset to page 1 whenever filters change.
url.searchParams.set('page', '1');
// Replace existing brand filters with the current selection.
url.searchParams.delete('brand');
for (const brand of filters.brands) {
url.searchParams.append('brand', brand);
}
return url.toString();
}
const nextUrl = buildProductUrl('https://shop.example.in/products?page=4', {
category: 'phones',
sort: 'price-low-to-high',
brands: ['samsung', 'oneplus']
});
console.log(nextUrl);
// 'https://shop.example.in/products?page=1&category=phones&sort=price-low-to-high&brand=samsung&brand=oneplus'
The exact parameter order may vary depending on how you add and modify values. Your server or frontend should not rely on query parameter order for correctness.
Practical workflow: read query parameters into an object
Sometimes you want to convert query parameters into an object for easier use in your application. Be careful with repeated keys: a plain object can lose information if you store only one value per key.
This helper stores repeated parameters as arrays.
function queryParamsToObject(searchParams) {
const result = {};
for (const [key, value] of searchParams) {
if (Object.prototype.hasOwnProperty.call(result, key)) {
const currentValue = result[key];
result[key] = Array.isArray(currentValue)
? [...currentValue, value]
: [currentValue, value];
} else {
result[key] = value;
}
}
return result;
}
const url = new URL('https://example.in/products?tag=mobile&tag=android&page=2');
const paramsObject = queryParamsToObject(url.searchParams);
console.log(paramsObject);
// { tag: ['mobile', 'android'], page: '2' }
This is useful for debugging and simple state management, but for complex applications you may prefer a schema-based parser that validates expected fields and types.
Using URLSearchParams in Node.js
Modern Node.js also supports the WHATWG URL API.
const url = new URL('https://api.example.in/search?q=nodejs&page=1');
url.searchParams.set('page', '2');
url.searchParams.append('lang', 'en');
console.log(url.href);
// 'https://api.example.in/search?q=nodejs&page=2&lang=en'
If you are parsing an incoming request path such as /search?q=nodejs, you need a base URL because new URL() requires an absolute URL unless you provide a base.
const requestPath = '/search?q=nodejs&page=1';
const url = new URL(requestPath, 'https://example.in');
console.log(url.pathname); // '/search'
console.log(url.searchParams.get('q')); // 'nodejs'
Do not treat query parsing as security validation. If a parameter controls database queries, redirects, file access, or API calls, validate it separately on the server.
Encoding: let the API handle special characters
One major benefit of URLSearchParams is correct encoding. You should usually pass normal strings and let the API serialize them.
const url = new URL('https://example.in/search');
url.searchParams.set('q', 'best phones under ₹30000');
url.searchParams.set('city', 'Bengaluru South');
console.log(url.toString());
// 'https://example.in/search?q=best+phones+under+%E2%82%B930000&city=Bengaluru+South'
When you read the values back, the API decodes them.
const url = new URL('https://example.in/search?q=best+phones+under+%E2%82%B930000');
console.log(url.searchParams.get('q'));
// 'best phones under ₹30000'
Avoid manually calling encodeURIComponent() before passing values to set() unless you have a specific reason. Double encoding can produce confusing output.
const url = new URL('https://example.in/search');
url.searchParams.set('q', encodeURIComponent('red shoes'));
console.log(url.toString());
// 'https://example.in/search?q=red%2520shoes'
The %25 in the output is the encoded form of %, which means the value was encoded twice.
Common mistakes and pitfalls
Passing a full URL to URLSearchParams directly
URLSearchParams is for query strings, not full URL parsing. If you have a full URL, use new URL() first.
// Avoid this for full URLs:
const wrong = new URLSearchParams('https://example.in/search?q=javascript');
console.log(wrong.get('q')); // null
// Prefer this:
const url = new URL('https://example.in/search?q=javascript');
console.log(url.searchParams.get('q')); // 'javascript'
Forgetting that values are strings
const url = new URL('https://example.in/articles?page=2');
console.log(url.searchParams.get('page') + 1); // '21'
console.log(Number(url.searchParams.get('page')) + 1); // 3
Convert and validate values before using them as numbers.
Assuming new URLSearchParams(url.search) updates the URL automatically
If you create a separate URLSearchParams object from url.search, changes to that separate object do not automatically update the original URL.
const url = new URL('https://example.in/search?q=old');
const params = new URLSearchParams(url.search);
params.set('q', 'new');
console.log(url.toString());
// 'https://example.in/search?q=old'
url.search = params.toString();
console.log(url.toString());
// 'https://example.in/search?q=new'
When possible, use url.searchParams directly to avoid this extra assignment.
Using query parameters for sensitive data
Do not put passwords, OTPs, API keys, session tokens, Aadhaar numbers, PAN details, or other sensitive data in URL parameters. URLs may appear in browser history, server logs, analytics tools, referrer headers, screenshots, and shared messages.
Assuming client-side changes are enough
A user can edit URL parameters manually. If your application receives ?role=admin or ?price=1, your backend must not trust it just because your frontend generated a different value. Always validate and authorize sensitive operations on the server.
Defensive best practices
- Define an allowlist of expected query parameter names.
- Validate types and ranges, such as
pagebeing a positive integer. - Set safe defaults when parameters are missing or invalid.
- Limit maximum lengths for search terms and free-text parameters.
- Avoid open redirects by validating redirect targets on the server.
- Keep sensitive values out of URLs.
- Use
replaceState()for frequent UI changes andpushState()for meaningful navigation. - Treat URL parsing as parsing only, not as complete security protection.
Here is a simple validator for pagination parameters:
function readPage(searchParams) {
const rawPage = searchParams.get('page');
const page = rawPage === null ? 1 : Number.parseInt(rawPage, 10);
if (!Number.isInteger(page) || page < 1 || page > 1000) {
return 1;
}
return page;
}
const url = new URL('https://example.in/articles?page=abc');
console.log(readPage(url.searchParams)); // 1
This is intentionally small. Real applications should validate every parameter according to the business rule it affects.
Conclusion
The URL and URLSearchParams APIs are the cleanest way to read and modify URL parameters in JavaScript. Use new URL() for complete URLs, work with url.searchParams for query changes, and rely on methods like get(), set(), append(), and delete() instead of fragile string manipulation.
For beginner and intermediate developers, the most important habits are simple: remember that query values are strings, handle repeated parameters intentionally, let the API manage encoding, and validate everything important on the server. With these patterns, you can build search pages, filters, pagination, and shareable links that are easier to maintain and less likely to break.
