Web API · 05 of 05

Let the platform parse it

Check structure without a homemade regular expression. Includes an older-browser fallback.

URL.canParse()Baseline Widely availableMDN URL.canParse()
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 demo

Try 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>
// Works with URL.canParse, with try/catch as an older-browser fallback.
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
<!-- Tailwind CSS v4. Utilities style the form; the native URL API owns parsing. -->
<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>