Menu

JavaScript URLs and Query Strings: URL and URLSearchParams

How to parse, build, and modify URLs in JavaScript with the URL and URLSearchParams APIs - without regex and without edge-case bugs.

This page includes runnable editors. Edit, run, and see output instantly.

Stop Parsing URLs With String Tricks

Before the URL API existed, people sliced URLs with split('?'), regexes, and hope. It mostly worked until a value contained an &, a =, a space, or a non-ASCII character - and then it didn't. The browser and Node both ship a proper parser. Use it.

One call, every part of the URL already split apart and decoded correctly. The constructor throws a TypeError on invalid input, which is usually what you want - a garbage URL should fail loudly rather than silently produce garbage downstream.

Reading Query Parameters

Every URL has a .searchParams property - a URLSearchParams object that knows how to read and write the query string:

A few things worth noticing:

  • Values come back already decoded. ?name=Ada%20Lovelace gives you "Ada Lovelace".
  • Everything is a string. "2" is not 2. Convert with Number() if you need a number.
  • Repeated keys are legal. get returns the first match; getAll returns every match.
  • Missing keys return null, not undefined - so ?? "default" works well with them.

Building a Query String

You can build a query string from scratch with URLSearchParams - no manual escaping, no joining with &:

Or construct it from an object - any iterable of [key, value] pairs works, and a plain object does too:

set vs append: set replaces any existing value for that key. append adds another one. Use append when a key can legitimately repeat (tags, filters); use set for single-valued parameters.

Modifying a URL

Because URL is a live object, changing searchParams updates .search and .href automatically:

This is the idiomatic way to append a query parameter to an existing URL. No "does the URL already have a ?" check, no worrying about whether to prepend & or ?.

You can change other parts of the URL the same way:

Iterating Over Parameters

URLSearchParams is iterable. for...of gives you [key, value] pairs, and there are the usual keys(), values(), and entries() helpers:

Note that repeated keys show up multiple times - you'll see tag = web and then tag = beginner as separate entries. That's faithful to the actual query string.

If you want a plain object for a quick debug print, Object.fromEntries works - but it collapses repeats, keeping only the last value:

Fine for debugging. Wrong if any key can repeat.

Relative URLs Need a Base

new URL("/search?q=js") on its own throws - a relative path isn't a valid URL by itself. Pass a base as the second argument:

The resolution rules are the same ones browsers use for <a href>: a leading / is absolute from the host, no slash is relative to the current path, .. walks up. Very handy when you're assembling API URLs from a configured base.

In the browser, window.location.href is a ready-made base for parsing the current page's URL:

const u = new URL(window.location.href);
const page = u.searchParams.get("page") ?? "1";

Handling Invalid URLs

The URL constructor throws on malformed input. That's useful - but it means you need try/catch when parsing anything the user typed or an external system sent:

Modern environments also expose URL.canParse(input) - a boolean check that avoids the try/catch dance when you just want to validate:

A Small Working Example

Putting it together - read the current filters from a URL, tweak them, and produce a new URL to navigate to:

Passing null deletes the parameter. Any other value sets or overwrites it. That's a pattern you'll end up writing in one form or another any time you build filter UIs, pagination, or deep links.

What to Take Away

  • new URL(string) parses a URL into labelled parts. It throws on garbage.
  • url.searchParams is a URLSearchParams - use get, getAll, set, append, delete, has.
  • Encoding happens for you. Don't reach for encodeURIComponent unless you're building strings by hand.
  • Pass a base URL as the second argument to resolve relative paths.
  • URL.canParse (or try/catch) is your validation tool for untrusted input.

Every time you're tempted to split a URL with .split('?') or pick a query parameter out with a regex, reach for these APIs instead. They're shorter, correct, and already in the runtime.

Frequently Asked Questions

How do I parse a URL in JavaScript?

Pass the string to the URL constructor: const u = new URL('https://example.com/path?x=1'). The resulting object exposes protocol, host, pathname, search, hash, and a searchParams helper. It throws on invalid URLs, so wrap it in try/catch when parsing untrusted input.

How do I get a query string parameter in JavaScript?

Use url.searchParams.get('name'). It returns the decoded value or null if the parameter isn't present. For parameters that can repeat (?tag=a&tag=b), use searchParams.getAll('tag') to get all values as an array.

What's the difference between URL and URLSearchParams?

URL parses and represents a whole URL - protocol, host, path, query, hash. URLSearchParams is just the query-string part, and you can use it standalone to build or parse a=1&b=2 strings. Every URL instance has a .searchParams property that's a URLSearchParams tied to that URL.

Do I need to encode query parameters manually?

No. URLSearchParams encodes keys and values automatically when you call set, append, or read back the string. That handles spaces, &, =, and Unicode correctly. Only reach for encodeURIComponent when you're building a string by hand, which you usually shouldn't.

Coddy programming languages illustration

Learn to code with Coddy

Get started