Web API · 05 of 05
Let the platform parse it
Check structure without a homemade regular expression. Includes an older-browser fallback.
- Experience kept
- Validation feedback for absolute and relative URLs
- Machinery removed
- A handwritten parser and its edge-case tests
- Decision
- Use the URL parser, with a constructor fallback
Live demoTry a URL, then break it.
THE REDUCTION
Same outcome. Less to coordinate.
A regular expression becomes a second, incomplete definition of what a URL is.
CURRENT PROJECT · JS
const urlPattern = /^(https?:\/\/)?[\w.-]+\.[a-z]{2,}(\/.*)?$/i;
const isValid = (value) => urlPattern.test(value);
WITH FEWER PARTS · JS
const isValid = (value, base = location.href) =>
URL.canParse?.(value, base) ?? canConstructUrl(value, base);
The fallback uses the same URL constructor semantics, so older browsers get the same answer rather than a different home-grown grammar.
ABOUT THE PATTERN
Why this works
URL parsing rules stay with the browser, including relative URLs and edge cases that a regular expression usually misses.
Decisions that matter
- This recipe accepts absolute URLs.
- Pass an explicit base if relative URLs are intended.
- Parsing does not validate permitted protocols, destinations, or reachability.
- Render user values with textContent, and keep try/catch for older browsers.
Read from the skill's own reference files at build time.
COPY, ADAPT, SHIP
The dependency-free implementation
HTML, CSS, and the JavaScript the interaction actually needs
STANDALONE HTML
<!doctype html>
<html lang="en"><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
<title>Native URL parsing</title>
<style>
*{box-sizing:border-box}body{margin:0;padding:28px;font:14px/1.5 system-ui;color:#182334;background:#f2f5f9}label{display:block;font-weight:600;margin-bottom:10px}input{font:inherit;width:100%;padding:13px;border:1px solid #c9d3e2;border-radius:8px;background:white}input:focus-visible{outline:3px solid #3866e8;outline-offset:3px}.result{margin-top:18px;background:white;border:1px solid #dce2eb;border-radius:12px;padding:16px;overflow-wrap:anywhere}#status{font-weight:600;margin:0 0 6px}#parts{color:#647186;margin:0}.hint{color:#647186;font-size:13px;margin-top:16px}
</style>
<label for="url">Try a URL</label><input id="url" type="text" inputmode="url" value="https://example.com/hello" spellcheck="false" aria-describedby="hint">
<div class="result" role="status"><p id="status">Enter a URL to inspect it.</p><p id="parts"></p></div>
<p class="hint" id="hint">Parsing checks structure. It does not establish that a destination is safe.</p>
<script>
function parseURL(value) {
if (typeof URL.canParse === 'function' && !URL.canParse(value)) return null;
try { return new URL(value); } catch { return null; }
}
const input = document.querySelector('#url');
function update() {
const url = parseURL(input.value);
document.querySelector('#status').textContent = url ? 'Parsable URL' : 'Not an absolute URL';
document.querySelector('#parts').textContent = url ? `${url.protocol} · ${url.hostname || '(no host)'} · ${url.pathname}` : 'Try https://example.com/hello';
}
input.addEventListener('input', update);
update();
</script></html>
Using Tailwind CSS v4 already? View the adapted example+
Optional integration · adapt utilities to your project tokens
TAILWIND HTML
<label for="url" class="block font-semibold text-slate-900">Try a URL</label>
<input
id="url"
type="text"
inputmode="url"
value="https://example.com/hello"
spellcheck="false"
aria-describedby="url-hint"
class="mt-2 w-full rounded-lg border border-slate-300 bg-white p-3 text-slate-900 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-600"
>
<div class="mt-5 rounded-xl border border-slate-200 bg-white p-4 [overflow-wrap:anywhere]" role="status">
<p id="status" class="font-semibold text-slate-900">Enter a URL to inspect it.</p>
<p id="parts" class="mt-1 text-slate-600"></p>
</div>
<p id="url-hint" class="mt-4 text-sm text-slate-600">
Parsing checks structure. It does not establish that a destination is safe.
</p>
<script>
function parseURL(value) {
if (typeof URL.canParse === 'function' && !URL.canParse(value)) return null;
try {
return new URL(value);
} catch {
return null;
}
}
const input = document.querySelector('#url');
const status = document.querySelector('#status');
const parts = document.querySelector('#parts');
function update() {
const url = parseURL(input.value);
status.textContent = url ? 'Parsable URL' : 'Not an absolute URL';
parts.textContent = url
? `${url.protocol} · ${url.hostname || '(no host)'} · ${url.pathname}`
: 'Try https://example.com/hello';
}
input.addEventListener('input', update);
update();
</script>