Ctrl + K
URLs21 min read

Working with Query Strings

A practical guide to working with URL query strings in JavaScript and web applications, covering parsing, building, encoding, updating parameters, repeated values, filtering, pagination, UTM parameters and common mistakes.

Published: 2026-10-05

Query strings are one of the simplest ways to attach structured data to a URL. They are used for search, filters, sorting, pagination, analytics tracking, API options and many other forms of URL-driven state.

A query string begins after the question mark in a URL. For example, in https://example.com/products?category=books&page=2, the query string is ?category=books&page=2. It contains two parameters: category and page.

Although query strings look like ordinary text, they have their own syntax and encoding rules. Working with them correctly means understanding how to parse existing URLs, create new query strings, update individual parameters, handle repeated values and avoid encoding the same data multiple times.

What Is a Query String?

A query string is the part of a URL that begins with ? and contains additional information for the requested resource. It is commonly made up of one or more name-value pairs separated by ampersands.

https://example.com/products?category=books&page=2&sort=price

The query string in this example is ?category=books&page=2&sort=price.

PartExampleRole
Question mark?Starts the query component
Parameter namecategoryIdentifies a value
Equals sign=Separates a name from its value
Parameter valuebooksContains the actual data
Ampersand&Separates parameters

Query String vs Query Parameter

The terms query string and query parameter are related but refer to different levels of the URL structure. The query string is the complete portion after the question mark, while a query parameter is an individual name-value pair inside it.

?category=books&page=2&sort=price

In this example, the complete query string contains three parameters: category=books, page=2 and sort=price.

Why Query Strings Are Useful

Query strings are especially useful when information should be represented directly in a URL. Unlike temporary in-memory application state, URL state can be copied, bookmarked, reloaded and shared with another person.

  • Search terms.
  • Product and content filters.
  • Sorting preferences.
  • Pagination state.
  • Language and locale settings.
  • API request options.
  • Analytics and UTM tracking.
  • Shareable application state.
  • Optional resource parameters.

A Basic Query String

The simplest query string contains one parameter.

https://example.com/search?q=javascript

Additional parameters are appended with an ampersand.

https://example.com/search?q=javascript&category=web&page=2

The server or client application decides what these parameters mean. URL syntax itself only defines how the query component is represented.

Reading a Query String Manually

A developer can technically parse a query string by splitting it on ampersands and equals signs. For example, a simple string could be processed manually with JavaScript.

const query = "category=books&page=2";

const pairs = query.split("&");

for (const pair of pairs) {
  const [key, value] = pair.split("=");

  console.log(key, value);
}

This approach can appear convenient, but it quickly becomes unreliable when values contain encoded characters, repeated parameters, empty values or additional equals signs. Modern URL APIs are a much better choice for normal application code.

Use URLSearchParams

URLSearchParams is a built-in JavaScript API specifically designed for working with query parameters. It can parse existing query strings and construct new ones while handling serialization and encoding.

const params = new URLSearchParams(
  "category=books&page=2&sort=price"
);

console.log(params.get("category"));
console.log(params.get("page"));
console.log(params.get("sort"));

The values returned by get() are strings. If an application expects a number, boolean or another type, it should explicitly parse and validate the value.

Getting a Single Parameter

const params = new URLSearchParams(
  "?category=books&page=2"
);

const category = params.get("category");
const page = params.get("page");

The leading question mark is optional when creating URLSearchParams, so both ?category=books and category=books can be parsed.

What Happens When a Parameter Is Missing?

If get() is called for a parameter that does not exist, URLSearchParams returns null.

const params = new URLSearchParams(
  "category=books"
);

const page = params.get("page");

console.log(page);

This is useful when parameters are optional. Application code can provide a default value or handle the missing state explicitly.

const page = params.get("page") ?? "1";

Checking Whether a Parameter Exists

The has() method checks whether a parameter exists.

const params = new URLSearchParams(
  "category=books&page=2"
);

if (params.has("page")) {
  console.log("Page parameter exists");
}

has() is useful when the difference between a missing parameter and an explicitly empty parameter matters.

Reading the Complete URL

The URL class can parse a complete URL and expose its query parameters through searchParams.

const url = new URL(
  "https://example.com/products?category=books&page=2"
);

console.log(url.search);
console.log(url.searchParams.get("category"));
console.log(url.searchParams.get("page"));

The search property contains the query component including the question mark, while searchParams provides structured access to individual parameters.

Building a Query String

URLSearchParams can also be used to create a query string from individual values.

const params = new URLSearchParams();

params.set("category", "books");
params.set("page", "2");
params.set("sort", "price");

const queryString = params.toString();

console.log(queryString);

The result can be appended to a URL when needed.

const url = `https://example.com/products?${queryString}`;

Using URLSearchParams with an Object

For simple query strings, URLSearchParams can be initialized directly from an object.

const params = new URLSearchParams({
  category: "books",
  page: "2",
  sort: "price",
});

console.log(params.toString());

This is convenient when the application already has a set of named values that should become query parameters.

set() and append()

Two important URLSearchParams methods are set() and append(). They are similar, but their behavior with existing names is different.

set() creates a parameter or replaces all existing values for that name.

const params = new URLSearchParams();

params.set("tag", "javascript");
params.set("tag", "react");

console.log(params.toString());

After the second set(), there is only one tag value.

append() adds another value while preserving existing values.

const params = new URLSearchParams();

params.append("tag", "javascript");
params.append("tag", "react");

console.log(params.toString());

Working with Repeated Parameters

Repeated parameters are useful when one name represents multiple values.

https://example.com/tools?tag=javascript&tag=react&tag=nextjs

URLSearchParams provides getAll() for retrieving all values associated with a name.

const params = new URLSearchParams(
  "tag=javascript&tag=react&tag=nextjs"
);

const tags = params.getAll("tag");

console.log(tags);

When designing an API, repeated parameters should be documented clearly. Some frameworks treat them as arrays, while others may use different conventions.

Deleting Parameters

The delete() method removes a parameter and all of its values.

const url = new URL(
  "https://example.com/products?category=books&page=2&sort=price"
);

url.searchParams.delete("page");

console.log(url.toString());

Using delete() is safer than removing text with replace() because the URL API understands the structure of the query string.

Updating Parameters

A parameter can be updated using set().

const url = new URL(
  "https://example.com/products?page=2&sort=price"
);

url.searchParams.set("page", "3");

console.log(url.toString());

This pattern is particularly useful for pagination and filter controls in web applications.

Sorting Query Parameters

URLSearchParams also provides sort(), which sorts parameters by their names.

const params = new URLSearchParams(
  "page=2&sort=price&category=books"
);

params.sort();

console.log(params.toString());

Sorting can be useful when a consistent serialized representation is desirable, for example when comparing URLs or generating deterministic strings. However, changing parameter order can matter to systems that treat the raw URL string as significant, so it should be done deliberately.

Encoding Query Values

Query values often contain spaces, punctuation or other characters that need URL encoding.

const params = new URLSearchParams();

params.set("q", "red shoes & boots");

console.log(params.toString());

The URL API handles the required serialization instead of requiring the developer to manually replace spaces and special characters.

Why Manual Concatenation Is Error-Prone

A query string can be created manually with string concatenation, but this becomes increasingly difficult to maintain as the number of parameters grows.

const url =
  "/products?category=" +
  encodeURIComponent(category) +
  "&page=" +
  encodeURIComponent(String(page)) +
  "&sort=" +
  encodeURIComponent(sort);

This can work for a small example, but it requires the developer to remember separators, encoding rules and type conversions. URLSearchParams centralizes these responsibilities.

Avoid Double Encoding

One of the most common query-string bugs is encoding a value more than once.

Original: red shoes
Encoded: red%20shoes
Double encoded: red%2520shoes

The second encoding changes the percent sign in %20 into %25. The receiving application may then see red%20shoes instead of red shoes.

⚠️ Keep values in their normal form while your application manipulates them. Let the URL or query-string API perform encoding when the value is serialized.

Decoding Query Parameters

When URLSearchParams reads a parameter, it gives you the decoded value.

const params = new URLSearchParams(
  "q=red%20shoes%20%26%20boots"
);

const query = params.get("q");

console.log(query);

Because URLSearchParams handles decoding, you generally should not call decodeURIComponent on the result again.

When decodeURIComponent Is Useful

decodeURIComponent is useful when you explicitly have an individually percent-encoded component and need to decode it yourself.

const encoded = "red%20shoes%20%26%20boots";
const decoded = decodeURIComponent(encoded);

console.log(decoded);

For normal query-string processing, however, URLSearchParams is usually preferable because it understands the surrounding query syntax.

Spaces, %20 and +

Spaces in query data can appear differently depending on the serialization format. Percent-encoding commonly represents a space as %20, while application/x-www-form-urlencoded uses + for spaces.

q=hello%20world
q=hello+world

This is one reason manually replacing spaces is a poor strategy. Let the API responsible for serialization determine the appropriate representation.

Working with Empty Values

A query parameter can have an empty value.

https://example.com/search?q=

URLSearchParams distinguishes this from a missing parameter.

const params = new URLSearchParams("q=");

console.log(params.has("q"));
console.log(params.get("q"));

The application should decide whether an empty value is valid, equivalent to a missing value or an error.

Parameters Without an Equals Sign

Query strings can also contain a name without an explicit equals sign.

https://example.com/products?debug

Different applications can assign different meanings to this form. When parsed by URLSearchParams, the parameter can be accessed as a name with an empty string value.

Boolean Query Parameters

Query strings contain text, so boolean values are usually represented explicitly.

https://example.com/products?featured=true&archived=false

The application must convert those strings into actual booleans if necessary.

const value = params.get("featured");

const featured = value === "true";

For more important inputs, explicit schema validation is preferable to relying on a simple comparison.

Numeric Query Parameters

Numbers also arrive as strings and should be parsed and validated before use.

const pageParam = params.get("page");
const page = Number(pageParam);

if (!Number.isInteger(page) || page < 1) {
  throw new Error("Invalid page");
}

Validation should also consider reasonable maximum values. A request such as page=999999999 may be syntactically valid but still be inappropriate for the application.

Query Strings for Search

Search pages are a common place to use query strings because the search term is naturally part of the URL.

https://example.com/search?q=javascript%20promises

This makes the search state shareable and allows browser navigation to preserve previous searches.

Query Strings for Filters

Filters can also be represented through parameters.

https://example.com/products?category=books&priceMax=50&inStock=true

A frontend can update these parameters whenever the user changes a filter. The URL then becomes a serialized representation of the current view.

Query Strings for Sorting

https://example.com/products?sort=price&order=asc

The exact convention is application-specific. Some systems use sort=price:asc, while others use separate field and direction parameters.

Query Strings for Pagination

Pagination is another natural use case for query strings.

https://example.com/tools?page=3&limit=24

Changing the page can update only the relevant query parameter while preserving the current filters and sorting options.

const url = new URL(window.location.href);

url.searchParams.set("page", "3");

window.history.pushState({}, "", url);

Framework-specific routing APIs are often preferable in a React or Next.js application, but the underlying URL manipulation works the same way.

Query Strings for API Requests

APIs frequently use query strings for optional request parameters such as filtering, pagination and sorting.

GET /api/products?category=books&page=2&limit=20 HTTP/1.1

The server can inspect these parameters and use them to determine which records to return.

Query Strings and Arrays

There is no universal syntax for representing arrays in a query string. Common approaches include repeated names, comma-separated values and bracket notation.

ConventionExample
Repeated values?tag=js&tag=react&tag=next
Comma-separated?tag=js,react,next
Bracket notation?tag[]=js&tag[]=react
Indexed notation?tag[0]=js&tag[1]=react

Repeated parameters are particularly straightforward with URLSearchParams because append() and getAll() explicitly support multiple values.

Query String Iteration

URLSearchParams is iterable, which makes it convenient to inspect every parameter.

const params = new URLSearchParams(
  "category=books&page=2&sort=price"
);

for (const [key, value] of params) {
  console.log(key, value);
}

This is useful for debugging, logging and generic URL-processing utilities.

keys(), values() and entries()

URLSearchParams also exposes iterators for keys, values and complete entries.

for (const key of params.keys()) {
  console.log(key);
}

for (const value of params.values()) {
  console.log(value);
}

for (const [key, value] of params.entries()) {
  console.log(key, value);
}

Converting Query Parameters to an Object

For simple cases, query parameters can be converted to an object.

const params = new URLSearchParams(
  "category=books&page=2"
);

const values = Object.fromEntries(params);

console.log(values);

This approach is convenient when every parameter name is unique. Repeated parameter names require additional handling because a plain object cannot represent multiple values under the same key without using an array.

Building a URL from Existing Query Parameters

When modifying a URL, it is often better to start with URL rather than manually extracting the query string.

const url = new URL(
  "https://example.com/products?category=books&page=2"
);

url.searchParams.set("sort", "price");

console.log(url.toString());

This preserves the existing URL structure while adding or updating the desired parameter.

Relative URLs and Query Strings

Query strings can also be used with relative URLs.

/products?category=books&page=2

Relative URLs are common in navigation because the browser resolves them against the current origin.

Query Strings and URL Fragments

A URL can contain both a query string and a fragment. The query comes before the fragment.

https://example.com/docs?lang=en#installation

The query component is ?lang=en, while the fragment is #installation. The fragment is not part of the query string.

Query Strings and UTM Parameters

UTM parameters are ordinary query parameters that follow a widely used naming convention for marketing analytics.

https://example.com/pricing?utm_source=newsletter&utm_medium=email&utm_campaign=launch
ParameterTypical purpose
utm_sourceIdentifies the traffic source
utm_mediumIdentifies the marketing medium
utm_campaignIdentifies the campaign
utm_termCommonly identifies a paid-search term
utm_contentDistinguishes links or creatives within a campaign

Because UTM parameters are part of the query string, they can be read and manipulated using the same URLSearchParams API as any other parameter.

Query Strings and Browser History

When query parameters represent application state, changing them through normal navigation can create useful browser history entries.

This allows users to move backward and forward between searches, filters or pagination states. Whether every change should create a history entry depends on the application. For example, a search submission may reasonably create a new history entry, while every keystroke in a live search field may not.

Query Strings in React Applications

React applications frequently use query strings to keep filters, sorting and pagination synchronized with the URL.

const params = new URLSearchParams();

params.set("category", category);
params.set("sort", sort);
params.set("page", String(page));

const href = `/tools?${params.toString()}`;

A URL-driven state model makes the current view shareable and allows the browser's navigation system to participate in state management.

Query Strings in Next.js

Next.js applications commonly use query strings for search pages, filtering, sorting and pagination. In the App Router, server-rendered pages can receive search parameters, while client components can read the current URL using routing APIs such as useSearchParams.

"use client";

import { useSearchParams } from "next/navigation";

export default function ToolsPage() {
  const searchParams = useSearchParams();

  const category = searchParams.get("category");
  const page = searchParams.get("page");

  return null;
}

When updating the URL in Next.js, use the routing APIs appropriate for the application rather than directly modifying window.history when framework navigation behavior also needs to be coordinated.

Validating Query String Data

Query parameters come from the URL and should generally be considered untrusted input. A parameter that looks like a number, enum or identifier still needs validation before being used by application logic.

const sort = params.get("sort");

const allowedSorts = ["name", "price", "date"];

if (sort !== null && !allowedSorts.includes(sort)) {
  throw new Error("Invalid sort parameter");
}

This is especially important when query parameters affect database queries, filesystem paths, redirects, authorization or resource limits.

Do Not Trust Query Parameters for Authorization

A query parameter can be changed by the user. It should never be treated as proof that the user has permission to perform an operation.

https://example.com/account?userId=123

The presence of userId=123 does not establish that the current user is allowed to access account 123. Authorization must be enforced independently on the server.

Sensitive Data in Query Strings

⚠️ Do not place passwords, API keys, session credentials or other sensitive secrets into query strings. URLs can appear in browser history, server logs, analytics systems, proxy logs and monitoring tools.

URL encoding does not make sensitive query data private. Percent-encoding only changes the representation of the value and can be reversed.

Query Strings and Caching

Query strings can affect HTTP and application caching because different query strings can represent different request URLs.

/products?page=1
/products?page=2
/products?page=3

If a CDN or application cache includes the query string in its cache key, these URLs can produce separate cached entries. Cache configuration should therefore account for which parameters actually affect the response.

Query Strings and SEO

Query strings can create useful indexable URLs, but they can also produce many URL variations for the same content.

https://example.com/article
https://example.com/article?utm_source=email
https://example.com/article?utm_source=social

Tracking parameters may create multiple URL representations of the same page. Websites should consider canonicalization and other SEO controls when query parameters do not represent separate content.

Canonical Query Strings

Some applications benefit from producing a consistent representation of equivalent query strings. This can include consistent parameter names, ordering, casing and handling of default values.

Canonicalization can be useful for caching, URL comparison, analytics and SEO, but it should be implemented according to the application's actual semantics. Two query strings that look similar are not automatically equivalent.

Removing Default Parameters

If an application uses page=1 as the default page, it may choose to omit page=1 from the canonical URL.

https://example.com/products
https://example.com/products?page=1

Whether these should be treated as equivalent depends on the application. If page=1 is simply the default representation, keeping the URL shorter can simplify canonicalization.

Debugging Query Strings

When a URL does not behave as expected, inspect the actual query parameters rather than relying only on what the interface appears to show.

const url = new URL(window.location.href);

console.log("Raw query:", url.search);

for (const [key, value] of url.searchParams) {
  console.log(key, value);
}

This can reveal missing parameters, unexpected values, repeated names and encoding problems.

Common Query String Bugs

  • Forgetting the question mark before the first parameter.
  • Using commas or spaces instead of ampersands between parameters.
  • Failing to encode special characters in values.
  • Encoding a value twice.
  • Calling decodeURIComponent on values already decoded by URLSearchParams.
  • Using set() when append() is required for repeated values.
  • Assuming query parameters are automatically numbers or booleans.
  • Ignoring duplicate parameter names.
  • Using different array conventions between API endpoints.
  • Putting secrets into query strings.
  • Trusting query parameters for authorization.
  • Ignoring query strings when designing cache behavior.

A Practical Query String Workflow

A reliable workflow starts by treating the URL as structured data rather than an ordinary string.

  • Parse the URL with the URL API when working with a complete URL.
  • Use searchParams to access individual query parameters.
  • Use get() for one value and getAll() for repeated values.
  • Use set() when replacing a value.
  • Use append() when intentionally adding another value.
  • Use delete() to remove a parameter.
  • Let URLSearchParams handle serialization and encoding.
  • Validate values before using them in application logic.
  • Keep sensitive information out of the query string.
  • Define consistent conventions for arrays, booleans, pagination and sorting.

Example: Building a Filtered Product URL

A product page might need to preserve a category, price limit, sorting mode and page number.

const params = new URLSearchParams();

params.set("category", "books");
params.set("maxPrice", "50");
params.set("sort", "price");
params.set("page", "2");

const url = `/products?${params.toString()}`;

If the user changes the sort order, the application can update only that parameter while keeping the other state intact.

params.set("sort", "rating");

const updatedUrl = `/products?${params.toString()}`;

Example: Preserving Existing Parameters

Sometimes a feature needs to add one parameter without removing the others already present in the URL.

const url = new URL(window.location.href);

url.searchParams.set("view", "grid");

console.log(url.toString());

This pattern is useful for interfaces where several independent controls contribute to the same URL state.

Query Strings vs Request Bodies

Query strings are best suited to relatively small pieces of request metadata such as filtering, sorting and pagination. They are not a universal replacement for HTTP request bodies.

DataCommon location
Search termQuery string
FilterQuery string
Sort orderQuery string
PaginationQuery string
Complex object for creationRequest body
Large structured payloadRequest body
Secret credentialDedicated secure mechanism

Query String Best Practices

  • Use URLSearchParams instead of manually splitting and joining query strings.
  • Use the URL API when working with complete URLs.
  • Keep parameter names consistent across related endpoints.
  • Document whether parameters are required or optional.
  • Document default values.
  • Define how repeated parameters and arrays are represented.
  • Validate all externally supplied values.
  • Avoid putting sensitive data into URLs.
  • Avoid double encoding and unnecessary manual decoding.
  • Consider SEO and caching when query parameters create multiple URL variants.
  • Keep URL-driven application state limited to information that is useful to preserve and share.

Frequently Asked Questions

What is a query string?

A query string is the portion of a URL that begins with ? and contains additional request data, usually as name-value pairs separated by ampersands.

What is the easiest way to work with query strings in JavaScript?

For most JavaScript applications, URLSearchParams is the appropriate API for creating, reading, updating and deleting query parameters.

What is the difference between set() and append()?

set() creates or replaces all existing values for a parameter name, while append() adds another value and preserves existing values with the same name.

How do I get all values for a repeated query parameter?

Use URLSearchParams.getAll(). For example, tag=javascript&tag=react can be read as an array of values with getAll('tag').

Are query parameters automatically numbers and booleans?

No. Query parameters are represented as text. Applications should explicitly parse and validate numeric, boolean and other typed values.

Should I manually encode query parameters?

Usually not when using URLSearchParams or the URL API. These APIs handle serialization and encoding for normal application use.

Can query strings contain sensitive information?

Technically yes, but sensitive information such as passwords, access tokens and private credentials should not be placed in URLs because URLs can be stored in browser history, logs, analytics systems and other infrastructure.

Helpful Query String Tools

A URL Query String Parser is useful for inspecting an existing URL and viewing its individual parameters and values. A Query Parameter Builder helps construct query strings from structured data, while a URL Builder can combine a query string with the rest of a URL. A URL Parser is useful when you need to inspect all URL components, not just the query. For marketing links, a UTM Builder can generate consistent tracking parameters without manually assembling the query string.

Conclusion

Query strings are a small but important part of modern web development. They allow URLs to carry search terms, filters, sorting options, pagination state, API parameters and analytics information in a form that can be shared and preserved by the browser.

For JavaScript applications, URL and URLSearchParams provide structured APIs for working with query strings. They are preferable to manually splitting strings or concatenating parameter values because they handle parsing, encoding and repeated parameters more reliably.

The most important habits are to treat query parameters as untrusted string data, encode them through the appropriate URL APIs, validate values before using them, define clear conventions for repeated parameters and arrays, and keep sensitive information out of URLs.

Once query strings are treated as structured URL data rather than ordinary text, tasks such as building filters, preserving application state, generating API requests and debugging URLs become considerably easier to manage.

Found an issue?

Found an error, outdated information, or something missing from this article? Let me know through the Contact page.

Your feedback helps improve our articles and keep them accurate and useful.