URI.js is a Javascript library for working with URLs.


I always want to shoot myself in the head when looking at code like the following:

var url = "http://example.org/foo?bar=baz";
var separator = url.indexOf('?') > -1 ? '&' : '?';
url += separator + encodeURIComponent("foo") + "=" + encodeURIComponent("bar");

I still can't believe javascript - the f**ing backbone-language of the web - doesn't offer an API for mutating URLs. Browsers (Firefox) don't expose the Location object (the structure behind window.location). Yes, one could think of decomposed IDL attributes as a native URL management library. But it relies on the DOM element <a>, it's slow and doesn't offer any convenience at all.

How about a nice, clean and simple API for mutating URIs:

var url = new URI("http://example.org/foo?bar=baz");
url.addQuery("foo", "bar");

URI.js is here to help with that.

// mutating URLs 
    // -> http://rodneyrehm@example.org/foo.html?hello=world 
    // -> http://example.org/foo.html?hello=world 
    // -> http://example.org/bar/foo.html?hello=world 
    // -> http://example.org/bar/foo.xml?hello=world 
    // -> http://example.org/bar/foo.xml 
    // -> http://example.com/bar/foo.xml 
  .query({ foo: "bar", hello: ["world", "mars"] });
    // -> http://example.com/bar/foo.xml?foo=bar&hello=world&hello=mars 
// cleaning things up 
    // -> ?foo=bar&foo=baz 
// working with relative paths 
    // -> ./baz.html 
    // -> ../baz.html 
    // -> /foo/bar/baz.html 
// URI Templates 
URI.expand("/foo/{dir}/{file}", {
  dir: "bar",
  file: "world.html"
// -> /foo/bar/world.html 

See the About Page and API Docs for more stuff.

URI.js (without plugins) has a gzipped weight of about 7KB - if you include all extensions you end up at about 13KB. So unless you need second level domain support and use URI templates, we suggest you don't include them in your build. If you don't need a full featured URI mangler, it may be worth looking into the much smaller parser-only alternatives listed below.

URI.js is available through npm, bower, Jam, spm and manually from the build page:

# using bower
bower install uri.js
# using Jam
jam install URIjs
# using npm
npm install URIjs
# using spm
spm install urijs

I guess you'll manage to use the build tool or follow the instructions below to combine and minify the various files into URI.min.js - and I'm fairly certain you know how to <script src=".../URI.min.js"></script> that sucker, too.

Install with npm install URIjs or add "URIjs" to the dependencies in your package.json.

// load URI.js 
var URI = require('URIjs');
// load an optional module (e.g. URITemplate) 
var URITemplate = require('URIjs/src/URITemplate');
    // -> ../baz.html 

Clone the URI.js repository or use a package manager to get URI.js into your project.

  paths: {
    URIjs: 'where-you-put-uri.js/src'
require(['URIjs/URI'], function(URI) {
  console.log("URI.js and dependencies: ", URI("//amazon.co.uk").is('sld') ? 'loaded' : 'failed');
require(['URIjs/URITemplate'], function(URITemplate) {
  console.log("URITemplate.js and dependencies: ", URITemplate._cache ? 'loaded' : 'failed');

See the build tool or use Google Closure Compiler:

// ==ClosureCompiler==
// @compilation_level SIMPLE_OPTIMIZATIONS
// @output_file_name URI.min.js
// @code_url http://medialize.github.io/URI.js/src/IPv6.js
// @code_url http://medialize.github.io/URI.js/src/punycode.js
// @code_url http://medialize.github.io/URI.js/src/SecondLevelDomains.js
// @code_url http://medialize.github.io/URI.js/src/URI.js
// @code_url http://medialize.github.io/URI.js/src/URITemplate.js
// ==/ClosureCompiler==

Documents specifying how URLs work:

Informal stuff

How other environments do things

Discussion on Hacker News

  • node-dom-urls passy's partial implementation of the W3C URL Spec Draft for Node
  • urlutils cofounders' window.URL constructor for Node

If you don't like URI.js, you may like one of the following libraries. (If yours is not listed, drop me a line…)

URI.js is published under the MIT license. Until version 1.13.2 URI.js was also published under the GPL v3 license - but as this dual-licensing causes more questions than helps anyone, it was dropped with version 1.14.0.

  • adding Hungarian second level domains - (Issue #159)
  • adding <audio src="…"> and <video src="…"> to supported DOM attributes - (Issue #160), (Issue #161)
  • fixing file://hostname/path parsing - (Issue #158)
  • fixing .decodePathSegment() to not throw malformed URI error - (Issue #163)
  • changes to package management manifests only
  • fixing IPv6 normalization (bad variable name) - (Issue #145)
  • adding grunt and jshint
  • changing code style to 2 spaces indentation, single quote strings
  • applying 'use strict'; everywhere
  • fixing jshint warnings
  • fixing regression for Node.js introduced by fixing unsafe eval by using UMD's root - (Issue #107)
  • fixing parser to accept malformed userinfo (non-encoded email address) - (Issue #108)
  • fixing IE9 compatibility with location import: URI(location)
  • fixing string character access for IE7 - (Issue #67), (Issue #68)
  • fixing build() to properly omit empty query and fragment (Issue #53)
  • adding .resource() as compound of [path, query, fragment]
  • adding jQuery 1.8.x compatibility for jQuery.URI.js (remaining backwards compatibility!)
  • adding default ports for gopher, ws, wss
  • adding .duplicateQueryParameters() to control if key=value duplicates have to be preserved or reduced (Issue #51)
  • updating Punycode.js to version 1.1.1
  • improving AMD/Node using UMD returnExports - (Issue #44, Issue #47)
  • fixing .addQuery("empty") to properly add ?empty - (Issue #46)
  • fixing parsing of badly formatted userinfo http://username:pass:word@hostname
  • fixing parsing of Windows-Drive-Letter paths file://C:/WINDOWS/foo.txt
  • fixing URI(location) to properly parse the URL - (Issue #52)
  • fixing type error for fragment abuse demos - (Issue #50)
  • adding documentation for various encode/decode functions
  • adding some pointers on possible problems with URLs to About URIs
  • adding tests for fragment abuse and splitting tests into separate scopes
  • adding meta-data for Jam and Bower

Note: QUnit seems to be having some difficulties on IE8. While the jQuery-plugin tests fail, the plugin itself works. We're still trying to figure out what's making QUnit "lose its config state".

  • fixing parsing of /wiki/Help:IPA - (Issue #49)
  • fixing strictEncodeURIComponent() to properly encode * to %2A
  • fixing IE9's incorrect report of img.href being available - (Issue #48)
  • adding Second Level Domain (SLD) Support - (Issue #17)
  • updating Punycode.js to version 0.3.0
  • adding edge-case tests ("jim")
  • fixing edge-cases in .protocol(), .port(), .subdomain(), .domain(), .tld(), .filename()
  • fixing parsing of hostname in .hostname()
  • adding .subdomain() convenience accessor
  • improving internal deferred build handling
  • fixing thrown Error for URI("http://example.org").query(true) - (Issue #6)
  • adding examples for extending URI.js for fragment abuse, see src/URI.fragmentQuery.js and src/URI.fragmentURI.js - (Issue #2)
  • Initial URI.js