The essentials
Quick reference
One focused task per row. Jump to the related section for complete, working examples.
| Use | Syntax | Examples |
|---|---|---|
| Resolve against a base | const url = new URL('../guide', document.baseURI) | View examples |
| Parse without throwing | const url = URL.parse(input, location.href) | View examples |
| Validate a destination | if (url.origin !== location.origin || url.protocol !== 'https:') throw new Error('Blocked') | View examples |
| Set one query value | url.searchParams.set('page', '2') | View examples |
| Append a repeated value | url.searchParams.append('tag', 'html') | View examples |
| Read repeated values | const tags = url.searchParams.getAll('tag') | View examples |
| Navigate to a fragment | location.hash = 'installation' | View examples |
| Observe fragment traversal | addEventListener('hashchange', event => render(location.hash)) | View examples |
| Add application history | history.pushState({ page: 2 }, '', '?page=2') | View examples |
| Replace the current entry | history.replaceState({ page: 1 }, '', '?page=1') | View examples |
| Restore traversed state | addEventListener('popstate', event => render(event.state)) | View examples |
| Intercept supported navigation | event.intercept({ handler: () => render(new URL(event.destination.url)) }) | View examples |
| Navigate with the new API | await navigation.navigate('/account').finished | View examples |
URLs are application state that users bookmark, share, reload, and traverse with browser controls. Parse them with the platform URL model, mutate query tuples rather than concatenating strings, and reserve fragments for addressable document state. When an application updates session history, it must also restore the matching interface during traversal. The newer Navigation API can centralize same-document routing where supported, but ordinary links and server-rendered destinations remain the dependable foundation.
Step by step
Detailed examples
Parse, resolve, and validate URLs as structured data
new URL(input, base) resolves relative references and throws TypeError for invalid input; URL.parse(input, base) is the newer non-throwing alternative and returns null, so feature-detect it when older engines matter. Inspect protocol, origin, hostname, port, pathname, search, and hash instead of testing raw strings. Parsing is not authorization: validate the exact schemes and origins your operation permits, reject embedded credentials, and never turn an untrusted next or redirect parameter into navigation without an allowlist. Visually confusable internationalized hosts are another reason to compare parsed origins rather than displayed substrings.
function safeDestination(input) {
let url;
try {
url = new URL(input, document.baseURI);
} catch {
return null;
}
if (url.protocol !== 'https:' || url.origin !== location.origin) return null;
if (url.username || url.password) return null;
return url;
}
const destination = safeDestination('/account?tab=security');
if (destination) console.log(destination.pathname); Treat a query as an ordered list of name-value tuples
URLSearchParams handles encoding and supports repeated names. set replaces all tuples with that name, append adds one, get returns the first, getAll returns every value, and delete removes matches. A URL object's searchParams is live: mutations update its serialized URL. The serializer follows application/x-www-form-urlencoded rules, encoding spaces as plus signs, and can change equivalent spelling such as percent escapes or tilde encoding; do not use the resulting href as a cryptographic signature unless you control canonicalization. Constructing URLSearchParams from a full URL string does not parse the URL—pass url.search or use new URL first.
const url = new URL(location.href);
url.searchParams.set('page', '2');
url.searchParams.delete('debug');
url.searchParams.append('tag', 'html');
url.searchParams.append('tag', 'accessibility');
console.log(url.searchParams.getAll('tag'));
console.log(url.href);
history.replaceState(history.state, '', url); Use fragments for addressable document state
A URL fragment identifies a location or state within the current resource and is not sent in an HTTP request. Normal anchor links such as href=#details provide shareable, keyboard-friendly navigation without script. Assigning location.hash normally creates a session-history entry; location.replace('#details') replaces instead. hashchange fires when the active entry's fragment changes through fragment navigation or traversal, but not when pushState or replaceState changes a URL. Avoid putting secrets in fragments: they remain visible, copyable, available to page scripts, and may reach analytics or downstream applications.
<nav aria-label="Account sections">
<a href="#profile">Profile</a>
<a href="#security">Security</a>
</nav>
<section id="profile"><h2>Profile</h2></section>
<section id="security"><h2>Security</h2></section>
<script>
addEventListener('hashchange', () => {
console.log('Active fragment:', location.hash.slice(1));
});
</script> Keep session history and rendered state synchronized
pushState adds an entry and replaceState updates the active entry; neither loads the URL nor dispatches popstate at call time. Their state is structured-cloned, must be serializable, and should remain small because user agents may impose limits. The URL must be one the current document is allowed to rewrite, which in ordinary HTTP(S) pages means staying on the same origin. popstate is for traversal such as Back, Forward, or history.go(), and event.state is the newly active entry's state. Store durable identifiers in history and re-fetch large or sensitive data; always be able to rebuild from the URL because state may be null after initial load or external navigation.
function showCatalog(url, state = {}) {
const category = url.searchParams.get('category') ?? 'all';
document.querySelector('#view').textContent = `Category: ${category}`;
}
function chooseCategory(category) {
const url = new URL(location.href);
url.searchParams.set('category', category);
history.pushState({ category }, '', url);
showCatalog(url, history.state);
}
addEventListener('popstate', event => {
showCatalog(new URL(location.href), event.state ?? {});
});
showCatalog(new URL(location.href), history.state ?? {}); Local code tester
Try URL state and history traversal
Change filters, inspect the generated query, and use Back and Forward to verify that the rendered view follows session history.
Sources and further reading
References
Authoritative documentation used to verify and expand this cheat sheet.
Help us improve
Found a typo or missing example?
Tell us what would make this cheat sheet clearer, more complete, or more useful.



