cf.completefrontendCode editorOpen lab
THE JAVASCRIPT FIELD GUIDE

URL & URLSearchParams

Learn to parse, build, encode, decode, validate, and safely edit URLs with URL and URLSearchParams instead of fragile string concatenation.

By the end you can
  • 01
    Read every URL partUse new URL(input, base) to inspect protocol, host, path, query, and hash.
  • 02
    Build query strings safelyUse live url.searchParams methods for filters, pages, and repeated keys.
  • 03
    Choose the right encoderCompare encodeURI, encodeURIComponent, decoding, URL.canParse, and URL.parse.

URLs are not just strings

A URL looks like text, but it is really a structured address. It can name a scheme, a host, a port, a path, a query string, and a hash. JavaScript gives you two built-in helpers for this job: URL for the address and URLSearchParams for the form-like query string after ?.

This matters any time code builds a link, redirects a user, saves a return URL, or calls an API. String concatenation is easy until a user searches for rock & roll, a page name contains a space, or a filter appears twice. The browser and Node.js already know the URL rules, so let them do the paperwork.

Plain definition

new URL(input, base?) parses an address into safe, editable parts. url.searchParams is a live URLSearchParams object for reading and writing the query string.

Real-life analogyA URL is a postal address with labeled parts

If you write one long address on an envelope, the mail service still reads it in labeled chunks. URLs work the same way. The punctuation is not decoration; it tells the parser where one part ends and the next begins.

In real life: Delivery service
In JavaScript: protocol, like https:
In real life: City and street
In JavaScript: host, like example.com
In real life: Apartment door
In JavaScript: port, like :8443
In real life: Rooms inside
In JavaScript: pathname, like /docs/url
In real life: A note with questions
In JavaScript: search, like ?q=url
In real life: A sticky tab in a book
In JavaScript: hash, like #examples

Where the analogy stops: Postal workers see every part of a postal address. A URL hash is different: browsers use it locally and do not send it to the server in an HTTP request.

If strings and Unicode are still fresh topics for you, keep the Strings and Unicode and strings lessons nearby. We will also use object conversion ideas from Object.keys, values and entries.

Parse with new URL()

INTERACTIVE

The constructor is strict on purpose. new URL(input) accepts an absolute URL. new URL(input, base) also accepts a relative input and resolves it against the base. If the result is not a valid URL, it throws a TypeError.

Once parsing succeeds, the object exposes normalized properties. href is the full address. origin is the scheme plus host and non-default port for HTTP(S). hostname uses punycode for internationalized domain names. port is the empty string when the URL uses the default port, like 443 for HTTPS. hash includes the leading #, but the hash is a browser-side fragment, not data sent to the server.

URL dissector
The code behind the dissectorPop out in the code editor (opens in a new tab)JavaScript
const url = new URL(input, base);console.log(url.href);console.log(url.protocol);console.log(url.hostname);console.log(url.searchParams.getAll("tag"));
Parsed partsvalid
href
https://example.com:8443/docs/page?tab=api#examples
origin
https://example.com:8443
protocol
https:
username
(empty)
password
(empty)
host
example.com:8443
hostname
example.com
port
8443
pathname
/docs/page
search
?tab=api
hash
#examples
tag values
(none)
Try it yourself

Parsed successfully. URL.canParse says true. Checking URL.parse in your browser….

Try credentials, IPv6, Unicode domains, repeated keys, hashes, and a relative URL with a base. The table shows exactly what the URL object stores.

Try the Unicode preset: the domain changes to punycode in hostname, while the path and query are percent-encoded in href. Try the credentials preset too. Usernames and passwords can appear in URLs, but showing or logging them is usually a security smell in real applications.

Build queries with URLSearchParams

INTERACTIVE

Query strings are tiny forms in the URL. A key names a box, and a value fills it: ?q=boots&page=2. The same key can appear more than once, which is common for filters: ?tag=js&tag=web.

Real-life analogyURLSearchParams is a form with labeled boxes

Instead of writing & and = by hand, fill in labeled boxes. URLSearchParams handles the special-character paperwork and serializes the form correctly.

In real life: A box labeled Search
In JavaScript: set('q', value)
In real life: Several checked topics
In JavaScript: repeated append('tag', value) calls
In real life: Erasing a field
In JavaScript: delete('page')
In real life: Putting forms in a neat order
In JavaScript: sort()

Where the analogy stops: A paper form can keep two boxes with the same label visually separate. When you convert params to an object with Object.fromEntries, duplicate labels collapse and the last value wins.

The searchParams property on a URL is live. Change it and url.search plus url.href update immediately. Use set to replace one value, append for repeated keys, delete to remove a key, has to check for a key, get for the first value, and getAll for every value. Iteration yields [name, value] pairs.

URLSearchParams lab
Building the queryPop out in the code editor (opens in a new tab)JavaScript
const url = new URL("https://example.com/search");url.searchParams.set("q", query);for (const tag of tags) url.searchParams.append("tag", tag);url.searchParams.set("page", page);url.searchParams.sort();console.log(url.href);console.log([...url.searchParams]);
Live queryspaces become +
url.href
https://example.com/search?q=rock+%26+roll+%F0%9F%8E%B8&tag=music&tag=live+shows&page=2
toString()
q=rock+%26+roll+%F0%9F%8E%B8&tag=music&tag=live+shows&page=2
getAll("tag")
["music","live shows"]
has("page")
true
iteration
[["q","rock & roll 🎸"],["tag","music"],["tag","live shows"],["page","2"]]
new URLSearchParams(object)
q=rock+%26+roll+%F0%9F%8E%B8&page=2
Object.fromEntries
{"q":"rock & roll 🎸","tag":"live shows","page":"2"}
Try it yourself

Live URL: https://example.com/search?q=rock+%26+roll+%F0%9F%8E%B8&tag=music&tag=live+shows&page=2

The searchParams object is live-linked to url.search. Edit a field and url.href changes immediately.

Notice the space rule: URLSearchParams serializes spaces as +, because query strings use the application/x-www-form-urlencoded tradition. It decodes those plus signs back to spaces when you read values through URLSearchParams.

Encoding and decoding

INTERACTIVE

URL punctuation has meaning. A raw & separates query parameters, = separates a name from a value, ? starts the query, # starts the fragment, and / separates path segments. When those characters are user data, they must be packed so they are not mistaken for structure.

Real-life analogyEncoding packs fragile items

The URL parser is like a shipping clerk. If fragile punctuation is loose in the box, it may be read as part of the address. Encoding wraps it as percent escapes such as %26.

In real life: Bubble-wrapping a glass
In JavaScript: percent-encoding a space, &, =, ?, #, or emoji
In real life: Leaving address labels visible
In JavaScript: encodeURI keeps URL punctuation
In real life: Wrapping one item completely
In JavaScript: encodeURIComponent protects a single value or segment

Where the analogy stops: Encoding is not encryption. Anyone can decode %26 back to &; the goal is unambiguous syntax, not secrecy.

Encoding face-off
Four encoders and decodersPop out in the code editor (opens in a new tab)JavaScript
encodeURIComponent(value);encodeURI(value);new URLSearchParams({ q: value }).toString();decodeURIComponent("%E0%A4%A"); // throws URIError
Resultsreal browser APIs
encodeURIComponent
rock%20%26%20roll%3F%20yes%2F%231%20%E2%98%95
encodeURI
rock%20&%20roll?%20yes/#1%20%E2%98%95
URLSearchParams
q=rock+%26+roll%3F+yes%2F%231+%E2%98%95
decodeURIComponent round trip
rock & roll? yes/#1 ☕
decodeURI round trip
rock & roll? yes/#1 ☕
+ through URLSearchParams
rock and roll
+ through decodeURIComponent
rock+and+roll
malformed %E0%A4%A
URIError
Try it yourself

Notice that encodeURI keeps URL punctuation like ?, &, =, /, and #, while encodeURIComponent wraps them. URLSearchParams uses + for spaces in query strings.

The malformed decode example is fixed text so the page can show the URIError without rendering broken characters.
Choosing a URL encoder
ToolBest forImportant behavior
encodeURIComponent(value)One query value, path segment, or hash valueEscapes reserved URL punctuation such as ?, &, =, /, and # as data.
encodeURI(url)A mostly complete URLLeaves reserved punctuation alone: ; , / ? : @ & = + $ #.
URLSearchParamsA whole query stringUses form encoding: spaces serialize as +, and values decode correctly through params.
decodeURI / decodeURIComponentUndoing the matching encodingBoth throw URIError on malformed percent escapes, and lone surrogates also make URI encoders throw.
Which encoder?
  • A single query value: rock & roll
  • A whole URL with a space in the path: https://example.com/a b?x=1
  • One path segment that literally contains /
  • Building a query from { q, page }
  • Filters like tag=js&tag=web
  • A hash value typed by a user: intro#part
Try it yourself
0 of 6 correct

Sort each task by the helper you would reach for first.

Choose a category for every card. You can change an answer at any time; Reset clears them all.

URL.canParse, URL.parse, and safer API URLs

STEP THROUGH

URL.canParse(input, base?) answers with a boolean instead of throwing. It is useful for form validation and optional user input. URL.parse(input, base?) is newer: it returns a URL object or null instead of throwing. Both are recent additions to browsers, and URL.parse is the newer one, so feature-detect it before relying on it in code that must run in older browsers.

A tiny parse-or-null helper

If URL.parse is missing, wrap new URL() in try/catch and return null on failure. That gives callers a value to branch on instead of an exception to catch.

Now watch the practical bug these helpers prevent. The unsafe line below concatenates a query string by hand. It works for boring words, but breaks as soon as the search text contains an ampersand.

Safe API URL vs fragile concatenation
Step 0 of 7Ready
Your turn: follow the blue line

Pick a search term, predict which URL survives special characters, then step through the recorded code.

Running in
  1. script
Next: line 1
Click the blue line to take the next stepPop out in the code editor (opens in a new tab)JavaScript
const unsafe = base + "?q=" + "rock & roll";const url = new URL(base);url.searchParams.set("q", "rock & roll");url.searchParams.append("tag", "music & culture");console.log(unsafe);console.log(url.href);
CallStoreChangeResultRun = next line. Ran = already executed.
Recent returnsNothing yet. Start with the blue line.
Search term on line 2 and line 4

Changing the term starts a fresh recorded run. Try the ampersand version first.

A guided replay recorded from real JavaScript calls, not an engine debugger. Step follows executed statements; Back reviews a snapshot. Reset starts a fresh run.

The safe version treats the search text as data. set and append encode the values, so the server receives exactly the user’s search and exactly the tag you intended.

Where you’ll use this

URLs appear in routing, sharing links, analytics dashboards, pagination, OAuth return addresses, image CDNs, and every lesson that fetches data from a server. The pattern is almost always the same: parse a trusted base, add path or query pieces with URL-aware APIs, and only then read href.

  • Search pages: start from new URL(location.href), change searchParams, then update the address bar.
  • API clients: start from a known base endpoint, append filters and page numbers, and pass url.href to fetch.
  • Redirect validation: use URL.canParse, then check origin before trusting a return URL.
  • Link generation: encode each dynamic path segment with encodeURIComponent if it might contain /.

Common misconceptions

  • “A URL is just a string.” It can be displayed as a string, but the parser knows structured rules that string methods do not.
  • “The hash goes to the server.” Browsers keep the fragment locally for navigation inside the page; HTTP requests do not include it.
  • “encodeURI and encodeURIComponent are interchangeable.” One preserves URL punctuation for a whole address; the other protects one piece of data.
  • “A plus sign always means a space.” It means space in form-encoded query strings, so URLSearchParams handles it. decodeURIComponent("+") returns +, not a space.
  • “Turning params into an object keeps repeated keys.” Object.fromEntries(new URLSearchParams("tag=js&tag=web")) keeps the last tag. Use getAll for every value.

Practice: URL objects

5 EXERCISES
Exercise 1 · Warm-upPredict normalized URL parts

Read the starter code. What does the first console.log print?

Starter codePop out in the code editor (opens in a new tab)JavaScript
const url = new URL("https://example.com:443/docs?q=hi#top");
console.log(url.origin);
console.log(url.port);
console.log(url.hash);

Answer, then press Check. Spacing and letter case don’t matter.

    Exercise 2 · PracticeSpot the empty port

    What does the second console.log print?

    Starter codePop out in the code editor (opens in a new tab)JavaScript
    const url = new URL("https://example.com:443/docs?q=hi#top");
    console.log(url.origin);
    console.log(url.port);
    console.log(url.hash);

    Answer, then press Check. Spacing and letter case don’t matter.

      Exercise 3 · PracticeBuild a safe search URL

      Run the code or reason it out. What encoded text appears for the tag value?

      Starter codePop out in the code editor (opens in a new tab)JavaScript
      const url = new URL("https://shop.example/search");
      url.searchParams.set("q", "red shoes");
      url.searchParams.append("tag", "sale & outlet");
      console.log(url.href);

      Answer, then press Check. Spacing and letter case don’t matter.

        Exercise 4 · PracticeFind the bug in a shared link

        A teammate writes "/search?q=" + search. Explain why it breaks for rock & roll, then rewrite it with URL.

          Exercise 5 · ChallengeRepeated keys vs objects

          What does Object.fromEntries(params).tag keep?

          Starter codePop out in the code editor (opens in a new tab)JavaScript
          const params = new URLSearchParams("tag=js&tag=web&page=2");
          console.log(params.getAll("tag").join(","));
          console.log(Object.fromEntries(params).tag);

          Answer, then press Check. Spacing and letter case don’t matter.

            Quiz: check your understanding

            7 QUESTIONS

            Answer once from your prediction, then read every explanation.

            Lesson quiz · 7 questionsScore: first tries count
            1. Question 1 of 7What does new URL('/docs', 'https://example.com/app/') create?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              console.log(new URL("/docs", "https://example.com/app/").href);

              Choose an answer to see the explanation.

            2. Question 2 of 7Which property omits the default HTTPS port 443?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              const url = new URL("https://example.com:443/a");
              console.log(url.port);

              Choose an answer to see the explanation.

            3. Question 3 of 7What does URLSearchParams use for spaces when it serializes a query?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              console.log(new URLSearchParams({ q: "rock and roll" }).toString());

              Choose an answer to see the explanation.

            4. Question 4 of 7Which method keeps repeated query values?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              const params = new URLSearchParams("tag=js&tag=web");
              console.log(params.getAll("tag").join("|"));

              Choose an answer to see the explanation.

            5. Question 5 of 7Which encoder should protect one path segment named reports/2026 so the slash is data?

              Choose an answer to see the explanation.

            6. Question 6 of 7What happens when decodeURIComponent('%E0%A4%A') runs?

              Choose an answer to see the explanation.

            7. Question 7 of 7What is URL.canParse('notes', 'https://example.com/app/')?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              console.log(URL.canParse("notes", "https://example.com/app/"));

              Choose an answer to see the explanation.

            Key takeaways

            • new URL(input, base?) parses, resolves, normalizes, and throws TypeError for invalid URLs.
            • url.searchParams is live-linked to url.search and is the safest way to build query strings.
            • URLSearchParams serializes spaces as +; encodeURIComponent uses %20.
            • encodeURI is for whole URLs; encodeURIComponent is for one piece of data.
            • URL.canParse returns a boolean, and URL.parse returns a URL or null when available.

            Final definition: URL objects turn address strings into editable, validated parts, and URLSearchParams turns query strings into safe labeled values.

            Up next: Streams & progress.

            CompleteFrontend Clear concepts. Working examples.