ufo

Lightweight, dependency-free URL utilities for parsing, joining, normalizing, and manipulating URLs across Node, browser, and Deno.

Library
npm
v1.6.4
1,301stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
57/100Fair
Development Activity52
Maintenance20
Community56
Maturity60
Momentum40

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
74/100Good
Architecture78
Code Quality85
Innovation68
Learning Curve65

ufo is a small, dependency-free utility library for working with URLs in JavaScript and TypeScript. It provides a comprehensive set of pure functions for parsing, encoding, joining, normalizing, and comparing URLs, covering edge cases like protocol-relative URLs, repeated query keys, punycode hostnames, and dangerous script protocols that the native URL API handles inconsistently or not at all.

Maintained by the unjs ecosystem (the team behind Nuxt, Nitro, and h3), ufo is used as a foundational primitive across many unjs packages and is downloaded tens of millions of times a week. It ships as a tiny, side-effect-free ESM/CJS dual package with full TypeScript types, making it a common building block for routers, HTTP clients, and static site generators that need consistent, predictable URL handling in Node.js, browsers, and Deno.

What You Get

  • Parsing utilities - parseURL, parsePath, parseAuth, parseHost, and parseFilename break a URL string into protocol, auth, host, pathname, search, and hash components without throwing on partial or relative input.
  • Encoding/decoding helpers - encode, encodePath, encodeHost, encodeQueryKey/Value, and their decode counterparts handle percent-encoding and punycode consistently across path, query, and hash segments.
  • Query string utilities - parseQuery and stringifyQuery convert between query strings and plain objects, correctly handling repeated keys as arrays.
  • URL manipulation helpers - joinURL, joinRelativeURL, resolveURL, withQuery, withProtocol, withBase/withoutBase, and slash/fragment helpers (withTrailingSlash, withoutHost, etc.) let you compose and rewrite URLs without manual string surgery.
  • Safety checks - isScriptProtocol flags dangerous blob:/data:/javascript:/vbscript: protocols, and hasProtocol/isRelative/isEqual support validating and comparing arbitrary URL-like strings.

Common Use Cases

  • Normalizing user-supplied or third-party URLs before storing or rendering them, including query string re-encoding and double-slash cleanup.
  • Building routers, middleware, or static-site generators (as used internally by Nuxt, Nitro, and h3) that need to join base paths with relative segments correctly.
  • Rewriting or redirecting URLs - swapping protocols, adding/removing trailing slashes, or stripping hosts - without pulling in the full WHATWG URL object.
  • Sanitizing links against script-injection protocols (javascript:, data:, vbscript:) in user-generated content pipelines.

Under The Hood

Architecture The package is organized as a flat set of five focused modules (encoding.ts, parse.ts, query.ts, url.ts, utils.ts) re-exported from a single index.ts, each exposing pure, stateless functions that operate on plain strings and a handful of small interfaces (ParsedURL, ParsedPath, ParsedAuth, ParsedHost, QueryObject) defined in parse.ts and query.ts. There is no class hierarchy or dependency injection beyond a deprecated $URL wrapper in url.ts, which itself composes the functional parse/encode/query utilities rather than introducing new state. Data flows one-directionally - a raw string goes in, a parsed or encoded string (or plain object) comes out - with no internal caching or shared global state, consistent with the package’s declared sideEffects: false. This makes any single module (for example, the regex-based parseURL) safely replaceable without rippling through the rest of the codebase.

Tech Stack Written in TypeScript targeting ESNext modules, built with unbuild (a Rollup-based bundler) into dual ESM (.mjs) and CJS (.cjs) outputs with generated .d.ts declarations, and using automd to keep the README’s function reference in sync with JSDoc comments in src. There are zero runtime dependencies; devDependencies cover linting (eslint plus eslint-config-unjs), formatting (prettier), testing (vitest with @vitest/coverage-v8 and compile-time assertions via a dedicated types.test-d.ts), and release automation (changelogen). The project uses pnpm as its package manager and runs CI on GitHub Actions (Node 20), executing lint, build, and a coverage-instrumented vitest run uploaded to Codecov on every push.

Code Quality Tests are extensive, spanning thirteen test files that each target a specific module (parsing, encoding, joining, normalizing, punycode, query strings, trailing slashes, and more), largely written as table-driven cases that enumerate many edge-case inputs per function rather than one-off assertions. Functions favor returning sensible defaults over throwing on malformed input (parseURL, parseHost, and parseAuth all degrade gracefully), naming is consistent and verb-first (parseX/encodeX/decodeX/withX/withoutX), and the entire surface is written in strict TypeScript with exported interfaces giving callers full type safety. ESLint and Prettier enforce style, and the test script chains linting, type-checking, and the vitest suite together in CI.

API Design ufo’s core idea is treating a URL as a set of small composable pure functions rather than a stateful object, which lets it parse malformed, relative, and protocol-relative strings that would throw with the native new URL() constructor. It fills real gaps in the WHATWG URL API with purpose-built helpers like joinRelativeURL, isScriptProtocol, and withBase/withoutBase. Naming stays highly consistent across roughly thirty exports, which keeps the learning curve manageable despite the surface area, and JSDoc comments carry runnable examples that automd re-injects directly into the README, keeping docs and code from drifting apart. It is a pragmatic ergonomics layer over existing URL parsing rules rather than an invention of new URL semantics.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search