[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"verticals":3,"quiz-url-parse-safe-parsing":32,"quiz-article-url-parse-safe-parsing":48},[4,20],{"id":5,"slug":6,"name":7,"tagline":8,"description":9,"accentFrom":10,"accentTo":11,"icon":12,"defaultLocale":13,"locales":14,"features":16,"position":19},"019fe637-3d33-714b-b57f-23e163ffca0c","dev","Web Development","Build. Learn. Ship.","Practical courses, engineering-grade articles and open-source tools for people who ship.","violet-500","cyan-400","◇","en",[13,15],"fa",{"courses":17,"paths":17,"articles":17,"exams":18,"flashcards":18,"packages":17,"community":17,"certificates":17,"teams":17,"commerce":17},true,false,0,{"id":21,"slug":22,"name":23,"tagline":24,"description":25,"accentFrom":26,"accentTo":10,"icon":27,"defaultLocale":13,"locales":28,"features":30,"position":31},"019fe637-3dc2-754c-8657-0f175bfee7c6","lang","Languages","Learn a language the way you learn a codebase.","Structured paths, listening drills and spaced repetition that actually sticks.","amber-400","⌘",[13,15,29],"es",{"courses":17,"paths":17,"articles":18,"exams":18,"flashcards":18,"packages":18,"community":17,"certificates":18,"teams":18,"commerce":17},2,{"id":33,"slug":34,"kind":35,"title":36,"description":37,"config":38,"verticalId":5,"vertical":43,"course":40,"_count":44,"access":45,"attempts":47,"questionCount":39},"019fe776-9ddd-746a-b9ce-ff46e56f6a7b","url-parse-safe-parsing","PRACTICE_QUIZ","URL.canParse() and URL.parse() — safe URL parsing","Parsing an untrusted URL used to mean wrapping new URL() in try\u002Fcatch. URL.canParse() and URL.parse() are the native alternatives. These questions check what each method returns on valid and invalid input, when to reach for each one, the double-parse antipattern, and the browser support timeline.",{"questionCount":39,"timeLimitSec":40,"shuffleQuestions":18,"shuffleOptions":17,"negativeMarking":19,"passScorePct":41,"maxAttempts":40,"revealAnswers":42,"allowFlagging":18,"allowBacktracking":17},7,null,70,"IMMEDIATE",{"slug":6,"name":7},{"questions":39},{"allowed":17,"reason":46},"FREE",[],{"id":49,"slug":34,"title":50,"subtitle":40,"excerpt":51,"coverUrl":52,"locale":13,"readingMinutes":53,"publishedAt":54,"viewCount":55,"likeCount":19,"commentCount":19,"author":56,"vertical":61,"topic":62,"tags":65,"_count":77,"playground":79,"body":81,"bodyMd":246,"seo":247,"translationGroupId":251,"thread":252,"assessments":254,"translations":257,"quiz":259},"019fe660-b8d7-7515-892c-bd576110158c","You've been wrapping `new URL()` in try\u002Fcatch. `URL.parse()` does it natively.","Validating an untrusted URL before parsing it used to mean a try\u002Fcatch wrapper or a regex. `URL.canParse()` checks validity without constructing. `URL.parse()` parses and returns null on failure. Both are Baseline 2024 — no wrapper needed.","\u002Fmedia\u002Fcovers\u002Furl-parse-safe-parsing.png",5,"2026-08-01T08:27:13.710Z",78,{"id":57,"name":58,"username":59,"avatarUrl":40,"headline":60},"019fe637-3c25-7088-9034-39c9f15dc3c8","Parsa Jiravand","parsa","Frontend engineer · building bestpractic",{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":63,"name":64},"javascript","JavaScript",[66,68,71,74],{"slug":63,"name":67,"color":40},"Javascript",{"slug":69,"name":70,"color":40},"webdev","Webdev",{"slug":72,"name":73,"color":40},"frontend","Frontend",{"slug":75,"name":76,"color":40},"typescript","Typescript",{"assessments":78},1,{"slug":34,"title":80},"URL.parse() vs new URL() — safe URL parsing",{"blocks":82,"version":78},[83,87,92,95,98,104,107,111,114,118,121,124,128,131,135,138,141,145,148,151,154,157,160,163,166,173,176,180,183,186,189,194,197,200,205,208,211,214,217,220,223,226,229,232,235,238],{"id":84,"html":85,"type":86},"b1","\u003Cp>Every codebase has this utility function somewhere. You wrote it the first time an untrusted URL caused an uncaught exception, or when a form field needed validation before submission, or when an API returned a path instead of a full URL. It&#39;s the try\u002Fcatch wrapper, and it works. The problem is that every team writes their own version, they differ in subtle ways, and they shouldn&#39;t need to exist. \u003Ccode>URL.canParse()\u003C\u002Fcode> and \u003Ccode>URL.parse()\u003C\u002Fcode> are the native replacements — shipping now in every modern browser.\u003C\u002Fp>","paragraph",{"id":88,"html":89,"text":90,"type":91,"level":31},"b2","The wrapper you&#39;ve been maintaining","The wrapper you've been maintaining","heading",{"id":93,"html":94,"type":86},"b3","\u003Cp>The \u003Ccode>new URL()\u003C\u002Fcode> constructor throws a \u003Ccode>TypeError\u003C\u002Fcode> on invalid input. That&#39;s correct when your input is a string literal you own — an exception is the right signal that your own code has a bug. It&#39;s the wrong behavior when you&#39;re parsing user input, a third-party API response, or a redirect URL from an HTTP header.\u003C\u002Fp>",{"id":96,"html":97,"type":86},"b4","\u003Cp>The result is this pattern:\u003C\u002Fp>",{"id":99,"code":100,"type":101,"language":102,"highlight":103},"b5","function safeParseURL(str, base) {\n  try {\n    return new URL(str, base);\n  } catch {\n    return null;\n  }\n}","code","js",[],{"id":105,"html":106,"type":86},"b6","\u003Cp>A bad URL in a form field is not a programmer error. Exceptions used for expected control flow make call sites harder to read and suppress details that would help debugging. This wrapper should not need to exist.\u003C\u002Fp>",{"id":108,"html":109,"text":110,"type":91,"level":31},"b7","\u003Ccode>URL.canParse()\u003C\u002Fcode> — validate without constructing","URL.canParse() — validate without constructing",{"id":112,"html":113,"type":86},"b8","\u003Cp>When you only need to know whether a string is a valid URL — form validation, link display logic, redirect safety checks — \u003Ccode>URL.canParse()\u003C\u002Fcode> gives you a boolean with no allocation:\u003C\u002Fp>",{"id":115,"code":116,"type":101,"language":102,"highlight":117},"b9","URL.canParse('https:\u002F\u002Fexample.com')           \u002F\u002F true\nURL.canParse('not a url')                     \u002F\u002F false\nURL.canParse('javascript:alert(1)')           \u002F\u002F true — valid URL, unsafe redirect\nURL.canParse('\u002Fabout', 'https:\u002F\u002Fexample.com') \u002F\u002F true\nURL.canParse('::bad', 'https:\u002F\u002Fexample.com')  \u002F\u002F false",[],{"id":119,"html":120,"type":86},"b10","\u003Cp>The second argument is a base URL, resolved the same way \u003Ccode>new URL()\u003C\u002Fcode> resolves it. This matters when you&#39;re validating relative paths — from a \u003Ccode>&lt;base&gt;\u003C\u002Fcode> tag, from API responses that return paths rather than full URLs, or from a \u003Ccode>&lt;a href&gt;\u003C\u002Fcode> value you want to check before following.\u003C\u002Fp>",{"id":122,"html":123,"type":86},"b11","\u003Cp>One important note: \u003Ccode>URL.canParse()\u003C\u002Fcode> validates the URL&#39;s \u003Cem>syntax\u003C\u002Fem>. It tells you whether the string can be parsed as a URL. It does not tell you whether the URL is safe to navigate to — \u003Ccode>javascript:\u003C\u002Fcode> and \u003Ccode>data:\u003C\u002Fcode> URLs are syntactically valid.\u003C\u002Fp>",{"id":125,"html":126,"text":127,"type":91,"level":31},"b12","\u003Ccode>URL.parse()\u003C\u002Fcode> — the non-throwing constructor","URL.parse() — the non-throwing constructor",{"id":129,"html":130,"type":86},"b13","\u003Cp>\u003Ccode>URL.parse()\u003C\u002Fcode> does what \u003Ccode>new URL()\u003C\u002Fcode> does but returns \u003Ccode>null\u003C\u002Fcode> instead of throwing when the input is invalid:\u003C\u002Fp>",{"id":132,"code":133,"type":101,"language":102,"highlight":134},"b14","const url = URL.parse(userInput);\nif (!url) {\n  showError('Not a valid URL.');\n  return;\n}\n\u002F\u002F url is a URL instance — all properties and methods are available\nconsole.log(url.hostname);\nconsole.log(url.pathname);\nurl.searchParams.set('ref', 'footer');",[],{"id":136,"html":137,"type":86},"b15","\u003Cp>Every property and method available on a \u003Ccode>URL\u003C\u002Fcode> object — \u003Ccode>hostname\u003C\u002Fcode>, \u003Ccode>pathname\u003C\u002Fcode>, \u003Ccode>searchParams\u003C\u002Fcode>, \u003Ccode>href\u003C\u002Fcode> — works the same way. The only difference from \u003Ccode>new URL()\u003C\u002Fcode> is what happens on invalid input: null instead of a thrown exception.\u003C\u002Fp>",{"id":139,"html":140,"type":86},"b16","\u003Cp>Like \u003Ccode>URL.canParse()\u003C\u002Fcode>, the second argument is a base URL:\u003C\u002Fp>",{"id":142,"code":143,"type":101,"language":102,"highlight":144},"b17","const url = URL.parse('\u002Fproducts\u002F42', 'https:\u002F\u002Fexample.com');\n\u002F\u002F → URL { href: 'https:\u002F\u002Fexample.com\u002Fproducts\u002F42', pathname: '\u002Fproducts\u002F42', ... }\n\nconst relative = URL.parse('..\u002Fimages\u002Fphoto.jpg', 'https:\u002F\u002Fcdn.example.com\u002Fassets\u002Fv2\u002F');\n\u002F\u002F → URL { href: 'https:\u002F\u002Fcdn.example.com\u002Fassets\u002Fimages\u002Fphoto.jpg', ... }",[],{"id":146,"html":147,"type":86},"b18","\u003C!-- playground:start -->",{"id":149,"html":150,"text":150,"type":91,"level":31},"b19","🎮 Try it yourself",{"id":152,"html":153,"type":86},"b20","\u003Cp>\u003Cstrong>\u003Ca href=\"https:\u002F\u002Fdaily-post-dev.netlify.app\u002Fposts\u002F2026-08-01-url-parse-safe-parsing\u002Fplayground\u002F\">▶️ Open the interactive playground →\u003C\u002Fa>\u003C\u002Fstrong>\u003C\u002Fp>",{"id":155,"html":156,"type":86},"b21","\u003Cp>\u003Cem>Runs right in your browser — poke at it and watch the concept react live.\u003C\u002Fem>\u003C\u002Fp>",{"id":158,"html":159,"type":86},"b22","\u003C!-- playground:end -->",{"id":161,"html":162,"text":162,"type":91,"level":31},"b23","Which to use and when",{"id":164,"html":165,"type":86},"b24","\u003Cp>There are now three ways to get a URL object, and each has a clear use case:\u003C\u002Fp>",{"id":167,"type":168,"items":169,"ordered":18},"b25","list",[170,171,172],"\u003Cstrong>\u003Ccode>new URL(str)\u003C\u002Fcode>\u003C\u002Fstrong> — when the input is yours and invalid input is a bug. Use this for URL construction from values you control. The thrown exception surfaces real mistakes immediately.","\u003Cstrong>\u003Ccode>URL.canParse(str)\u003C\u002Fcode>\u003C\u002Fstrong> — when you only need validity, not the URL object. Form validation, display logic, redirect checking. Zero allocation — it doesn&#39;t construct anything.","\u003Cstrong>\u003Ccode>URL.parse(str)\u003C\u002Fcode>\u003C\u002Fstrong> — when you need the URL object and the input might be invalid. This replaces every \u003Ccode>safeParseURL()\u003C\u002Fcode> wrapper in your codebase.",{"id":174,"html":175,"type":86},"b26","\u003Cp>The common antipattern to avoid: calling \u003Ccode>URL.canParse()\u003C\u002Fcode> immediately before \u003Ccode>URL.parse()\u003C\u002Fcode> or \u003Ccode>new URL()\u003C\u002Fcode>. That parses the string twice.\u003C\u002Fp>",{"id":177,"code":178,"type":101,"language":102,"highlight":179},"b27","\u002F\u002F Antipattern: double-parse\nif (URL.canParse(input)) {\n  const url = new URL(input); \u002F\u002F parsing the same string again\n  use(url);\n}\n\n\u002F\u002F Correct: parse once, check for null\nconst url = URL.parse(input);\nif (url) {\n  use(url);\n}",[],{"id":181,"html":182,"type":86},"b28","\u003Cp>Reach for \u003Ccode>URL.canParse()\u003C\u002Fcode> when null is the only thing you need to check — for example, gating a conditional that doesn&#39;t need the URL properties at all.\u003C\u002Fp>",{"id":184,"html":185,"text":185,"type":91,"level":31},"b29","TypeScript support",{"id":187,"html":188,"type":86},"b30","\u003Cp>TypeScript added type signatures for both methods in \u003Cstrong>version 5.1\u003C\u002Fstrong> (May 2023). No extra \u003Ccode>@types\u003C\u002Fcode> package is needed — if your \u003Ccode>tsconfig.json\u003C\u002Fcode> includes \u003Ccode>&quot;DOM&quot;\u003C\u002Fcode> in the \u003Ccode>lib\u003C\u002Fcode> array, they&#39;re typed automatically:\u003C\u002Fp>",{"id":190,"code":191,"type":101,"language":192,"highlight":193},"b31","function getCanonicalHost(input: string): string | null {\n  const url = URL.parse(input);\n  return url ? url.hostname : null;\n}","ts",[],{"id":195,"html":196,"type":86},"b32","\u003Cp>TypeScript knows \u003Ccode>URL.parse()\u003C\u002Fcode> returns \u003Ccode>URL | null\u003C\u002Fcode>, so the null check narrows the type to \u003Ccode>URL\u003C\u002Fcode> before you access its properties. No cast needed.\u003C\u002Fp>",{"id":198,"html":199,"text":199,"type":91,"level":31},"b33","Browser support",{"id":201,"type":168,"items":202,"ordered":18},"b34",[203,204],"\u003Ccode>URL.canParse()\u003C\u002Fcode>: \u003Cstrong>Baseline Widely Available\u003C\u002Fstrong> — Chrome 120 (January 2024), Firefox 115 (July 2023), Safari 17 (September 2023). Also available in Node.js 19.9 and Deno 1.33.","\u003Ccode>URL.parse()\u003C\u002Fcode>: \u003Cstrong>Baseline 2024\u003C\u002Fstrong> — Chrome 126 (June 2024), Firefox 126 (June 2024), Safari 18 (September 2024). Node.js 22.1.",{"id":206,"html":207,"type":86},"b35","\u003Cp>Both are safe for production in modern browser targets. For legacy support, the \u003Ccode>safeParseURL\u003C\u002Fcode> pattern above is still the correct polyfill — but with a clear feature-detect, you can retire it progressively.\u003C\u002Fp>",{"id":209,"html":210,"type":86},"b36","\u003C!-- quiz:start -->",{"id":212,"html":213,"text":213,"type":91,"level":31},"b37","🧠 Test yourself",{"id":215,"html":216,"type":86},"b38","\u003Cp>Think it clicked? \u003Cstrong>\u003Ca href=\"https:\u002F\u002Fdaily-post-dev.netlify.app\u002Fquiz\u002Ftake.html?post=2026-08-01-url-parse-safe-parsing\">Take the 7-question quiz →\u003C\u002Fa>\u003C\u002Fstrong>\u003C\u002Fp>",{"id":218,"html":219,"type":86},"b39","\u003Cp>\u003Cem>Instant feedback, a hint on every question, and an explanation for each answer — right or wrong.\u003C\u002Fem>\u003C\u002Fp>",{"id":221,"html":222,"type":86},"b40","\u003C!-- quiz:end -->",{"id":224,"html":225,"text":225,"type":91,"level":31},"b41","The takeaway",{"id":227,"html":228,"type":86},"b42","\u003Cp>Search your codebase for functions named \u003Ccode>safeParseURL\u003C\u002Fcode>, \u003Ccode>tryParseUrl\u003C\u002Fcode>, \u003Ccode>parseUrlSafely\u003C\u002Fcode>, or any utility with \u003Ccode>new URL()\u003C\u002Fcode> inside a try\u002Fcatch block. Each one can be replaced with a direct call to \u003Ccode>URL.parse()\u003C\u002Fcode>: same return type, same base-URL support, no wrapper to maintain, and no subtle behavioral difference between implementations written by different developers at different times.\u003C\u002Fp>",{"id":230,"html":231,"type":86},"b43","\u003Cp>Keep \u003Ccode>new URL()\u003C\u002Fcode> only where the input is guaranteed to be valid — URL construction from your own literals, config values, or template strings. Let the exception do its job there, and let \u003Ccode>URL.parse()\u003C\u002Fcode> handle the cases where invalid input is expected.\u003C\u002Fp>",{"id":233,"type":234},"b44","divider",{"id":236,"html":237,"type":86},"b45","\u003Cp>\u003Cem>Thanks for reading! Let&#39;s stay connected:\u003C\u002Fem>\u003C\u002Fp>",{"id":239,"type":168,"items":240,"ordered":18},"b46",[241,242,243,244,245],"⭐ \u003Cstrong>GitHub\u003C\u002Fstrong> — follow me and star the projects: \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fparsajiravand\">github.com\u002Fparsajiravand\u003C\u002Fa>","💬 \u003Cstrong>Discord\u003C\u002Fstrong> — join the frontend best-practices community: \u003Ca href=\"https:\u002F\u002Fdiscord.gg\u002Fd9KRhuAwQ\">discord.gg\u002Fd9KRhuAwQ\u003C\u002Fa>","📸 \u003Cstrong>Instagram\u003C\u002Fstrong> — frontend best practices, daily: \u003Ca href=\"https:\u002F\u002Fwww.instagram.com\u002Fbestpractice___\u002F\">@bestpractice___\u003C\u002Fa>","💼 \u003Cstrong>LinkedIn\u003C\u002Fstrong> — \u003Ca href=\"https:\u002F\u002Fwww.linkedin.com\u002Fin\u002Fparsa-jiravand\u002F\">linkedin.com\u002Fin\u002Fparsa-jiravand\u003C\u002Fa>","✉️ \u003Cstrong>Email\u003C\u002Fstrong> (work &amp; contract inquiries): \u003Ca href=\"mailto:bestpractice2026@gmail.com\">bestpractice2026@gmail.com\u003C\u002Fa>","Every codebase has this utility function somewhere. You wrote it the first time an untrusted URL caused an uncaught exception, or when a form field needed validation before submission, or when an API returned a path instead of a full URL. It's the try\u002Fcatch wrapper, and it works. The problem is that every team writes their own version, they differ in subtle ways, and they shouldn't need to exist. `URL.canParse()` and `URL.parse()` are the native replacements — shipping now in every modern browser.\n\n## The wrapper you've been maintaining\n\nThe `new URL()` constructor throws a `TypeError` on invalid input. That's correct when your input is a string literal you own — an exception is the right signal that your own code has a bug. It's the wrong behavior when you're parsing user input, a third-party API response, or a redirect URL from an HTTP header.\n\nThe result is this pattern:\n\n```js\nfunction safeParseURL(str, base) {\n  try {\n    return new URL(str, base);\n  } catch {\n    return null;\n  }\n}\n```\n\nA bad URL in a form field is not a programmer error. Exceptions used for expected control flow make call sites harder to read and suppress details that would help debugging. This wrapper should not need to exist.\n\n## `URL.canParse()` — validate without constructing\n\nWhen you only need to know whether a string is a valid URL — form validation, link display logic, redirect safety checks — `URL.canParse()` gives you a boolean with no allocation:\n\n```js\nURL.canParse('https:\u002F\u002Fexample.com')           \u002F\u002F true\nURL.canParse('not a url')                     \u002F\u002F false\nURL.canParse('javascript:alert(1)')           \u002F\u002F true — valid URL, unsafe redirect\nURL.canParse('\u002Fabout', 'https:\u002F\u002Fexample.com') \u002F\u002F true\nURL.canParse('::bad', 'https:\u002F\u002Fexample.com')  \u002F\u002F false\n```\n\nThe second argument is a base URL, resolved the same way `new URL()` resolves it. This matters when you're validating relative paths — from a `\u003Cbase>` tag, from API responses that return paths rather than full URLs, or from a `\u003Ca href>` value you want to check before following.\n\nOne important note: `URL.canParse()` validates the URL's *syntax*. It tells you whether the string can be parsed as a URL. It does not tell you whether the URL is safe to navigate to — `javascript:` and `data:` URLs are syntactically valid.\n\n## `URL.parse()` — the non-throwing constructor\n\n`URL.parse()` does what `new URL()` does but returns `null` instead of throwing when the input is invalid:\n\n```js\nconst url = URL.parse(userInput);\nif (!url) {\n  showError('Not a valid URL.');\n  return;\n}\n\u002F\u002F url is a URL instance — all properties and methods are available\nconsole.log(url.hostname);\nconsole.log(url.pathname);\nurl.searchParams.set('ref', 'footer');\n```\n\nEvery property and method available on a `URL` object — `hostname`, `pathname`, `searchParams`, `href` — works the same way. The only difference from `new URL()` is what happens on invalid input: null instead of a thrown exception.\n\nLike `URL.canParse()`, the second argument is a base URL:\n\n```js\nconst url = URL.parse('\u002Fproducts\u002F42', 'https:\u002F\u002Fexample.com');\n\u002F\u002F → URL { href: 'https:\u002F\u002Fexample.com\u002Fproducts\u002F42', pathname: '\u002Fproducts\u002F42', ... }\n\nconst relative = URL.parse('..\u002Fimages\u002Fphoto.jpg', 'https:\u002F\u002Fcdn.example.com\u002Fassets\u002Fv2\u002F');\n\u002F\u002F → URL { href: 'https:\u002F\u002Fcdn.example.com\u002Fassets\u002Fimages\u002Fphoto.jpg', ... }\n```\n\n\u003C!-- playground:start -->\n\n## 🎮 Try it yourself\n\n**[▶️ Open the interactive playground →](https:\u002F\u002Fdaily-post-dev.netlify.app\u002Fposts\u002F2026-08-01-url-parse-safe-parsing\u002Fplayground\u002F)**\n\n_Runs right in your browser — poke at it and watch the concept react live._\n\n\u003C!-- playground:end -->\n\n## Which to use and when\n\nThere are now three ways to get a URL object, and each has a clear use case:\n\n- **`new URL(str)`** — when the input is yours and invalid input is a bug. Use this for URL construction from values you control. The thrown exception surfaces real mistakes immediately.\n- **`URL.canParse(str)`** — when you only need validity, not the URL object. Form validation, display logic, redirect checking. Zero allocation — it doesn't construct anything.\n- **`URL.parse(str)`** — when you need the URL object and the input might be invalid. This replaces every `safeParseURL()` wrapper in your codebase.\n\nThe common antipattern to avoid: calling `URL.canParse()` immediately before `URL.parse()` or `new URL()`. That parses the string twice.\n\n```js\n\u002F\u002F Antipattern: double-parse\nif (URL.canParse(input)) {\n  const url = new URL(input); \u002F\u002F parsing the same string again\n  use(url);\n}\n\n\u002F\u002F Correct: parse once, check for null\nconst url = URL.parse(input);\nif (url) {\n  use(url);\n}\n```\n\nReach for `URL.canParse()` when null is the only thing you need to check — for example, gating a conditional that doesn't need the URL properties at all.\n\n## TypeScript support\n\nTypeScript added type signatures for both methods in **version 5.1** (May 2023). No extra `@types` package is needed — if your `tsconfig.json` includes `\"DOM\"` in the `lib` array, they're typed automatically:\n\n```ts\nfunction getCanonicalHost(input: string): string | null {\n  const url = URL.parse(input);\n  return url ? url.hostname : null;\n}\n```\n\nTypeScript knows `URL.parse()` returns `URL | null`, so the null check narrows the type to `URL` before you access its properties. No cast needed.\n\n## Browser support\n\n- `URL.canParse()`: **Baseline Widely Available** — Chrome 120 (January 2024), Firefox 115 (July 2023), Safari 17 (September 2023). Also available in Node.js 19.9 and Deno 1.33.\n- `URL.parse()`: **Baseline 2024** — Chrome 126 (June 2024), Firefox 126 (June 2024), Safari 18 (September 2024). Node.js 22.1.\n\nBoth are safe for production in modern browser targets. For legacy support, the `safeParseURL` pattern above is still the correct polyfill — but with a clear feature-detect, you can retire it progressively.\n\n\u003C!-- quiz:start -->\n\n## 🧠 Test yourself\n\nThink it clicked? **[Take the 7-question quiz →](https:\u002F\u002Fdaily-post-dev.netlify.app\u002Fquiz\u002Ftake.html?post=2026-08-01-url-parse-safe-parsing)**\n\n_Instant feedback, a hint on every question, and an explanation for each answer — right or wrong._\n\n\u003C!-- quiz:end -->\n\n## The takeaway\n\nSearch your codebase for functions named `safeParseURL`, `tryParseUrl`, `parseUrlSafely`, or any utility with `new URL()` inside a try\u002Fcatch block. Each one can be replaced with a direct call to `URL.parse()`: same return type, same base-URL support, no wrapper to maintain, and no subtle behavioral difference between implementations written by different developers at different times.\n\nKeep `new URL()` only where the input is guaranteed to be valid — URL construction from your own literals, config values, or template strings. Let the exception do its job there, and let `URL.parse()` handle the cases where invalid input is expected.\n\n---\n\n*Thanks for reading! Let's stay connected:*\n\n- ⭐ **GitHub** — follow me and star the projects: [github.com\u002Fparsajiravand](https:\u002F\u002Fgithub.com\u002Fparsajiravand)\n- 💬 **Discord** — join the frontend best-practices community: [discord.gg\u002Fd9KRhuAwQ](https:\u002F\u002Fdiscord.gg\u002Fd9KRhuAwQ)\n- 📸 **Instagram** — frontend best practices, daily: [@bestpractice___](https:\u002F\u002Fwww.instagram.com\u002Fbestpractice___\u002F)\n- 💼 **LinkedIn** — [linkedin.com\u002Fin\u002Fparsa-jiravand](https:\u002F\u002Fwww.linkedin.com\u002Fin\u002Fparsa-jiravand\u002F)\n- ✉️ **Email** (work & contract inquiries): [bestpractice2026@gmail.com](mailto:bestpractice2026@gmail.com)",{"title":248,"canonical":249,"description":250},"You've been wrapping `new URL()` in try\u002Fcatch. `URL.parse()` does it n","https:\u002F\u002Fbestpractic.org\u002Fblog\u002Furl-parse-safe-parsing","Validating an untrusted URL before parsing it used to mean a try\u002Fcatch wrapper or a regex. `URL.canParse()` checks validity without constructing. `URL.parse()` parses and returns n","019fe660-b8d7-7515-892c-c017e9255619",{"id":253,"locked":18},"019fe660-bf95-700e-b260-65579534228d",[255],{"id":33,"slug":34,"title":36,"_count":256},{"questions":39},[258],{"locale":13,"slug":34},{"id":33,"slug":34,"title":36,"_count":260,"questionCount":39},{"questions":39}]