JavaScript URL API Table

PieceWhat it doesField note
new URL(str)Parses into componentsThrows SyntaxError on garbage - wrap in try/catch for user input
u.searchParams.get(k)Read one query paramDecodes automatically - never manually unescape again
u.searchParams.set(k, v)Write a paramEncodes automatically; set replaces, append duplicates
u.searchParams.has(k)Presence checkDistinguishes ?a= from absence
u.pathname / u.hostPath and host separatelyNo string slicing between ? and #
u.hashFragment including #Empty string when absent - not null
URLSearchParams(u.search)Standalone param iteratorentries(), forEach, and delete() that URL lacks directly
u.toString()Serialize backRound-trips: parse, edit, serialize - encoding stays consistent
Reference: the MDN URL API reference. The API killed the last legitimate use for regex URL parsing: parse once into a URL object, edit through searchParams, serialize back - encoding and re-encoding stay consistent because the browser does them. The two habits that break hand-rolled code: splitting the query on & (values containing & explode) and forgetting that hash changes do not reload the page. Bottom line: if you are writing indexOf('?') in 2026, it is a bug in waiting - the URL object exists in every browser and Node. Related tools: URL encoder for the component encoding this API automates, fetch table for the requests that take these URLs, and extract urls for pulling candidates out of raw text first.

The URL API is the browser's built-in URL parser: new URL(str) validates and splits a URL into components, searchParams reads and writes the query string with automatic encoding, and toString() serializes it all back. The table below is the working eight - everything real code needs from the last legitimate use of regex URL parsing.

Bottom line: parse once, edit through searchParams, serialize back - encoding stays consistent because the browser does it. Hand-rolled parsing fails in two predictable places: splitting the query on & explodes when a value contains an encoded &-lookalike, and forgetting that hash changes never reload the page turns subtle bugs into invisible ones. The URL object exists in every browser and Node since 2017; indexOf('?') code is a bug in waiting.

The honest part: the two quirks worth memorizing are both small. URL throws a SyntaxError on unparseable input - user-supplied URLs need try/catch. And u.hash is an empty string (not null) when no fragment exists, which makes truthiness checks work by accident and strict comparisons fail by design.

How to use

  1. Parse with new URL(str) inside try/catch - user input is untrusted and garbage URLs throw.
  2. Read and write query values exclusively through searchParams: get, set, append, delete, has cover every case with automatic encoding.
  3. Serialize with toString() or href - the round trip parse-edit-serialize is the whole API's promise.

Frequently asked questions

Why should I use the URL API instead of splitting on & and =?

Because query values legitimately contain encoded separators: a search for 'a&b' arrives as ?q=a%26b, and naive splitting on & survives that only by luck of the encoding - unescape first and the value explodes into two parameters. searchParams decodes each value exactly once, handles missing = signs, keeps repeated keys (getAll), and re-encodes on write. Every hand-rolled parser eventually meets a value it mangles; the API cannot be fed a value it mangles.

What is the difference between searchParams.get and has?

has answers whether the parameter exists at all; get returns its value or null. They disagree at ?flag= (exists, empty-string value: has is true, get returns '') and ?flag (exists with no value at all: same result). The distinction matters for toggles where presence itself is the signal - ?dark in a URL meaning 'force dark' - and for debugging APIs that distinguish empty from missing.

How do I add or change a query parameter without reloading?

Parse the current URL, edit searchParams, then history.replaceState(null, '', u) - the address bar updates without navigation. The full pattern: const u = new URL(location.href); u.searchParams.set('page', '2'); history.replaceState(null, '', u). This is how filter UIs and pagination keep URLs shareable and the back button meaningful - the URL becomes state storage instead of a location.

Why does my URL constructor throw on inputs that look fine?

Relative strings like /page?id=1 have no base to resolve against - new URL('/page') throws because a URL needs a scheme. Fix with the two-argument form: new URL('/page', location.origin) resolves the relative path against a base. User-supplied values throw for real reasons too (missing scheme, spaces) - the throw is the API telling you the string is not a URL, which plain string checks were silently guessing at.

Related tools