ShopperCove
Menu
All writingBlogTopicsCategoriesAboutRSS
Blog
Categories
Observability & SRE62All categories
About

Plate 33

  1. Blog

ufo vs URL: 2.4 KB vs 0.5 KB Gzip

Aditya Challa·5 October 2026·5 min read

Hands-on
On this page
  1. What I tested
  2. How much does ufo cost?
  3. Do the outputs match?
  4. Is joinURL speed enough to justify ufo?
  5. Which one should you pick?
  6. Sources
  7. Related

I bundled URL helpers on this box: ufo 1.6.4 came out at 2,374 bytes gzip for six helpers, while a small WHATWG URL shim covering the same calls came out at 519 bytes. joinURL favored ufo (~0.22 µs vs ~2.5 µs). normalizeURL favored the URL constructor (~0.47 µs vs ~3.4 µs) because ufo does not resolve ./ and ../.

Short answer: pick ufo when you want string-first join/query helpers that stay relative-friendly and match the unjs stack. Keep native URL when you need real path normalization, default-port stripping, and zero extra bytes. No affiliate links in this post.

What I tested

  • Machine: 8 vCPU Intel Xeon / 15 GB RAM shared Linux cloud box, tested 6 Oct 2026 about 02:15 IST.
  • Versions: ufo 1.6.4, Node 22.20.0 WHATWG URL, esbuild 0.28.2 browser ESM minify + gzip -9.
  • Inputs: join of CDN base + three segments; withQuery on /search with a space and page key; parseURL with host:443, query, and hash; normalizeURL with mixed case and ./ ../ segments. Speed: 200,000 calls, median of 9 rounds, two order-reversed runs. Scripts in /workspace/bench-ufo-url/.
  • Findings: ufo core 5,041 / 2,374 B vs shim 1,195 / 519 B. joinURL ~227.7 / 221.6 ns (ufo) vs ~2,488 / 2,593 ns (URL shim). withQuery ~2,256 / 2,351 vs ~1,693 / 1,673. parseURL ~564 / 539 vs ~639 / 609. normalizeURL ~3,296 / 3,491 vs ~476 / 461.
  • One surprise: ufo.normalizeURL("https://A.com/./b/../c") left the dots in place; new URL resolved to https://a.com/c and lowercased the host. parseURL also kept host a.com:443 on ufo while URL dropped the default HTTPS port.
  • Not tested: browsers other than the esbuild browser target, unicode hostnames, IPv6 literals, or createURL / $URL class paths.

How much does ufo cost?

Build (esbuild minify, gzip -9)Minifiedgzip
ufo six helpers (joinURL, withQuery, parseURL, normalizeURL, withoutTrailingSlash, getQuery)5,041 B2,374 B
WHATWG URL shim with the same six names1,195 B519 B

Native URL is already in the runtime, so the true incremental cost of "just use URL" is closer to zero than 519 B. The shim exists only so the size table compares similar export surfaces. Track deltas the same way as size-limit vs bundlesize. Sibling unjs vs stdlib posts include pathe vs node:path and scule vs change-case.

Do the outputs match?

Checkufo 1.6.4WHATWG URL shim
joinURL('/api','v1','users')/api/v1/users/api/v1/users
joinURL('https://a.com/','/b/','c')​https://a.com/b/c​​https://a.com/b/c​
withQuery('/x', {q:'a b', n:1})/x?q=a+b&n=1/x?q=a+b&n=1
parseURL host for :443a.com:443a.com
normalizeURL dots / case​https://A.com/./b/../c​​https://a.com/c​
withoutTrailingSlash('/docs/')/docs/docs

Join and withQuery matched on my fixtures. Behavior diverges on default ports and on path-segment cleanup. If your app already leans unjs for HTTP, compare ky vs axios. Cookie-header helpers sit nearby in cookie-es vs cookie.

Is joinURL speed enough to justify ufo?

Cost (200k calls, median of 9, 2 runs, ns/op)ufoURL shim
joinURL227.7 / 221.62,488.3 / 2,593.2
withQuery2,255.5 / 2,350.81,693.2 / 1,673.4
parseURL563.6 / 539.0638.5 / 609.1
normalizeURL3,295.8 / 3,491.3475.8 / 461.4

joinURL was about 11× faster on ufo here because it stays in string space. withQuery and normalizeURL favored the URL constructor. For route builders that call join thousands of times per request, that join win is real. For one-off redirects, size and normalize behavior matter more. Hashing and clone helpers in the same series: ohash vs object-hash and klona vs structuredClone.

Which one should you pick?

SituationMy pick
Nuxt / unjs stack, relative joins, query helpersufo
Need ./ ../ resolution and host lowercasingWHATWG URL
Bundle budget under a few kilobytes for URL utilsWHATWG URL
Hot joinURL in a CDN/asset path builderufo
Parsing absolute URLs with default portsdecide on port policy first, then pick

Bottom line: ufo 1.6.4 was 2,374 B gzip for six helpers versus 519 B for a URL shim, with joinURL about 11× faster and normalizeURL much slower because dots stay literal. Who should not add ufo: apps that only call new URL a handful of times and already rely on WHATWG normalization. Config merge next door: defu vs lodash.merge.

How this was made: I installed ufo on the ShopperCove box, wrote a matching URL shim, measured esbuild bundles, timed four helpers in two order-reversed runs, and compared outputs on shared fixtures. The JSON results sit next to the scripts. The write-up was drafted with AI help and checked against that output.

Sources

  • https://github.com/unjs/ufo
  • https://developer.mozilla.org/en-US/docs/Web/API/URL
  • https://www.npmjs.com/package/ufo
  • https://nodejs.org/api/url.html

Related

  • https://www.shoppercove.com/blog/size-limit-vs-bundlesize
  • https://www.shoppercove.com/blog/pathe-vs-node-path
  • https://www.shoppercove.com/blog/scule-vs-change-case
  • https://www.shoppercove.com/blog/ky-vs-axios
  • https://www.shoppercove.com/blog/cookie-es-vs-cookie
  • https://www.shoppercove.com/blog/ohash-vs-object-hash
  • https://www.shoppercove.com/blog/klona-vs-structuredclone
  • https://www.shoppercove.com/blog/defu-vs-lodash-merge
ufourlbundle sizeperformancejavascriptweb developmentunjs

Lab evidence

What I found running this

Hands-on on ShopperCove box 6 Oct 2026 ~02:15 IST (8 vCPU Intel Xeon / 15 GB shared Linux, Node 22.20.0). ufo 1.6.4 vs WHATWG URL shim, esbuild 0.28.2 browser ESM minify + gzip -9. Size: ufo six helpers 5,041/2,374 B vs shim 1,195/519 B. Speed (200,000 calls, 9-round median, order-reversed, ns/op): joinURL ufo 227.7 / 221.6 vs URL 2,488.3 / 2,593.2; withQuery 2,255.5 / 2,350.8 vs 1,693.2 / 1,673.4; parseURL 563.6 / 539.0 vs 638.5 / 609.1; normalizeURL 3,295.8 / 3,491.3 vs 475.8 / 461.4. Behavior: join/withQuery matched; ufo.normalizeURL left ./ ../; URL resolved and lowercased host; parseURL host a.com:443 vs a.com. Not tested: other browsers, unicode hosts, IPv6, createURL/$URL. No affiliate.

Notes when a lab post goes up

Occasional email for new hands-on reviews. No sequence and no sponsors.

Related links

  • Plate 35

    cookie-es vs cookie: 2.3 KB vs 1.4 KB Gzip

    5 Oct 2026

  • Plate 89

    Dayjs vs date-fns: 3.5 KB vs 6.9 KB Gzip

    5 Oct 2026

  • Plate 81

    scule vs change-case: 570 B vs 795 B Gzip

    5 Oct 2026

On this page

  1. What I tested
  2. How much does ufo cost?
  3. Do the outputs match?
  4. Is joinURL speed enough to justify ufo?
  5. Which one should you pick?
  6. Sources
  7. Related
All writingBlogCategoriesTopicsAboutPrivacyRSS

© 2026 ShopperCove