How to Parse URLs in JavaScript: The URL API vs Manual Parsing
Source: Dev.to
Using the URL class
const url = new URL('https://example.com:8080/path/page?q=hello+world&lang=en#section');
url.protocol; // "https:"
url.hostname; // "example.com"
url.port; // "8080"
url.host; // "example.com:8080"
url.pathname; // "/path/page"
url.search; // "?q=hello+world&lang=en"
url.hash; // "#section"
url.origin; // "https://example.com:8080"
url.href; // full URL string
The parser handles edge cases automatically. Standard ports are omitted from host:
new URL('http://example.com:80/').host; // "example.com"
Query parameters with URLSearchParams
Never split on & and = manually—values can contain encoded & and = characters. Use URLSearchParams instead:
const url = new URL('https://example.com?q=hello+world&tags=js&tags=css');
const params = url.searchParams;
params.get('q'); // "hello world" (decodes `+` as space)
params.getAll('tags'); // ["js", "css"]
params.has('tags'); // true
// Iterate all params
for (const [key, value] of params) {
console.log(key, value);
}
// q hello world
// tags js
// tags css
Building a query string
const params = new URLSearchParams({
q: 'hello world',
sort: 'date',
page: '1'
});
params.toString(); // "q=hello+world&sort=date&page=1"
Appending without replacing
params.append('tags', 'javascript');
params.append('tags', 'css');
params.toString(); // "q=hello+world&sort=date&page=1&tags=javascript&tags=css"
Modifying an existing URL
const url = new URL('https://example.com?page=1&sort=date');
url.searchParams.set('page', '2'); // update
url.searchParams.delete('sort'); // remove
url.searchParams.append('filter', 'active'); // add
url.href; // "https://example.com/?page=2&filter=active"
Resolving relative URLs
new URL('/about', 'https://example.com').href;
// "https://example.com/about"
new URL('../images/photo.jpg', 'https://example.com/blog/post/').href;
// "https://example.com/blog/images/photo.jpg"
new URL('?page=2', 'https://example.com/articles').href;
// "https://example.com/articles?page=2"
This is the correct way to resolve relative links when scraping or building crawlers.
Percent‑encoding automatically
const url = new URL('https://example.com/search');
url.searchParams.set('q', 'café & creme brûlée');
url.href;
// "https://example.com/search?q=caf%C3%A9+%26+creme+br%C3%BBl%C3%A9e"
Manual encoding/decoding
// Encode a component value (e.g., query parameter value)
encodeURIComponent('hello world & more');
// "hello%20world%20%26%20more"
// Encode a full URL (preserves :, /, ?, =, &, #, @)
encodeURI('https://example.com/path?q=hello world');
// "https://example.com/path?q=hello%20world"
// Decode
decodeURIComponent('hello%20world');
// "hello world"
Validating URLs
function isValidUrl(string) {
try {
new URL(string);
return true;
} catch {
return false;
}
}
isValidUrl('https://example.com'); // true
isValidUrl('not a url'); // false
isValidUrl('//example.com'); // true (protocol‑relative)
If you need to require a specific protocol:
function isHttpUrl(string) {
try {
const url = new URL(string);
return url.protocol === 'http:' || url.protocol === 'https:';
} catch {
return false;
}
}
Hostname vs. host
new URL('https://sub.example.co.uk/path').hostname;
// "sub.example.co.uk"
new URL('https://example.com:443/path').host; // "example.com:443"
new URL('https://example.com:443/path').hostname; // "example.com"
hostname strips the port; host includes it.
Node.js compatibility
The URL class is globally available in Node.js since v10. For older versions:
const { URL, URLSearchParams } = require('url');
It is also available in Web Workers and Service Workers in the browser.
Legacy <a> element trick (browser only)
Before the URL API, developers used a DOM <a> element to parse URLs:
const a = document.createElement('a');
a.href = 'https://example.com/path';
a.hostname; // "example.com"
This approach works only in the browser, can behave inconsistently across browsers for edge cases, and does not support URLSearchParams. The modern URL API should be preferred.
Conclusion
The URL API, together with URLSearchParams, eliminates a whole category of bugs related to query‑string handling and edge‑case URL parsing. If you’re still manually splitting on ? and &, switch to the API—you’ll wonder how you ever parsed URLs without it.