path-to-regexp vs query-string vs uri-template vs url-template
URL Parsing, Matching, and Templating Utilities in JavaScript
path-to-regexpquery-stringuri-templateurl-templateSimilar Packages:

URL Parsing, Matching, and Templating Utilities in JavaScript

path-to-regexp, query-string, uri-template, and url-template are utilities for handling different parts of a URL in JavaScript applications. path-to-regexp focuses on matching path strings against patterns and extracting parameters, commonly used in routing. query-string specializes in parsing and stringifying the query portion of a URL (the part after ?). uri-template and url-template deal with expanding templates into full URLs based on RFC 6570 or custom syntax, allowing developers to construct URLs dynamically from objects. While path-to-regexp and query-string are mainstream choices for routing and query management, the template libraries serve specific needs for hypermedia APIs or complex URL construction.

Npm Package Weekly Downloads Trend

3 Years

Github Stars Ranking

Stat Detail

Package
Downloads
Stars
Size
Issues
Publish
License
path-to-regexp08,59659.7 kB92 months agoMIT
query-string06,90459 kB212 days agoMIT
uri-template044-25 years agoMIT
url-template01947.99 kB12 years agoBSD-3-Clause

URL Parsing, Matching, and Templating Utilities in JavaScript

When building web applications, handling URLs correctly is critical for routing, data fetching, and state management. The ecosystem offers several specialized tools: path-to-regexp, query-string, uri-template, and url-template. Each solves a different part of the URL puzzle. Let's break down their specific roles, capabilities, and trade-offs.

🗂️ Core Purpose: Matching vs. Parsing vs. Templating

These libraries fall into three distinct categories. Understanding which problem you are solving is the first step.

path-to-regexp is for matching and extracting.
It turns a path pattern (like /users/:id) into a regular expression. You use it to check if a URL fits a pattern and to grab the values.

// path-to-regexp: Match and extract
import { match } from 'path-to-regexp';

const matchUser = match('/users/:id');
const result = matchUser('/users/123');
// result.params = { id: '123' }

query-string is for parsing and stringifying.
It handles the part of the URL after the ?. It converts between a string and an object, handling encoding and arrays.

// query-string: Parse and stringify
import queryString from 'query-string';

const parsed = queryString.parse('?filter=shoes&sort=price');
// { filter: 'shoes', sort: 'price' }

const stringified = queryString.stringify({ page: 1, limit: 10 });
// 'page=1&limit=10'

uri-template and url-template are for constructing URLs.
They take a template string with placeholders and fill them with data to create a full URL.

// uri-template: RFC 6570 standard expansion
import { parse } from 'uri-template';

const template = parse('/users/{id}');
const url = template.expand({ id: 123 });
// '/users/123'
// url-template: Flexible placeholder replacement
import urlTemplate from 'url-template';

const template = urlTemplate.parse('/posts/{postId}/comments/{commentId}');
const url = template.expand({ postId: 1, commentId: 5 });
// '/posts/1/comments/5'

🛠️ Feature Deep Dive: Path Matching and Parameters

If your goal is routing (deciding which component to show based on the URL), path-to-regexp is the clear winner. The template libraries are not designed for matching incoming requests.

path-to-regexp supports modifiers like optional parameters, wildcards, and repeating groups.

// path-to-regexp: Advanced patterns
import { pathToRegexp } from 'path-to-regexp';

// Optional parameter
const re1 = pathToRegexp('/users/:id?'); 

// Wildcard
const re2 = pathToRegexp('/files/*'); 

// Repeating parameter
const re3 = pathToRegexp('/tags/:tag+'); 

query-string does not handle paths. It only handles the query segment. Trying to use it for routing will fail.

// query-string: Path handling
// Does NOT support path matching
const parsed = queryString.parseUrl('/users/123?role=admin');
// Returns { url: '/users/123', query: { role: 'admin' } }
// You still need another tool to parse '/users/123'

uri-template and url-template focus on expansion, not matching. They assume you already know the structure.

// uri-template: No matching capability
// Designed for expansion only
const template = parse('/users/{id}');
// Cannot test if '/users/123' fits this template

🔤 Feature Deep Dive: Query Parameter Handling

When dealing with filters, search, or pagination, you need robust query string handling. Native URLSearchParams is good, but query-string offers more control.

query-string handles arrays, objects, and sorting out of the box.

// query-string: Array and object handling
import queryString from 'query-string';

// Arrays
queryString.stringify({ tags: ['js', 'node'] });
// 'tags=js&tags=node' (default)

// Sorting keys
queryString.stringify({ b: 1, a: 2 }, { sort: true });
// 'a=2&b=1'

path-to-regexp ignores query strings by default. It focuses on the pathname.

// path-to-regexp: Query string behavior
import { match } from 'path-to-regexp';

const matchFn = match('/search');
matchFn('/search?q=term'); 
// Matches, but params will NOT include 'q'
// You must parse query separately

uri-template and url-template can include query parameters in the template, but they don't parse incoming query strings.

// uri-template: Query in template
const template = parse('/search{?q,page}');
const url = template.expand({ q: 'test', page: 1 });
// '/search?q=test&page=1'

⚠️ Maintenance and Standards Compliance

Long-term stability matters when choosing infrastructure code.

path-to-regexp is highly maintained and widely adopted.
It is the engine behind Express.js and React Router. It follows semantic versioning strictly.

query-string is also highly maintained by a trusted author (sindresorhus).
It is updated regularly to handle new browser behaviors and edge cases.

uri-template adheres to RFC 6570.
This is a formal standard. If you are building a public API or hypermedia client, this compliance is valuable. However, the npm package ecosystem for RFC 6570 is fragmented.

url-template is less standardized.
It offers a simpler syntax but may not behave consistently with other RFC-compliant tools. Check the specific repository for activity before adopting in large projects.

🌐 Real-World Scenarios

Scenario 1: Building a Custom Router

You need to match incoming browser paths to components.

  • Best choice: path-to-regexp
  • Why? It converts patterns to regex for fast matching and extracts params cleanly.
// Router implementation
const routes = [
  { pattern: '/users/:id', component: UserPage },
  { pattern: '/posts/:slug', component: PostPage }
];

function resolveRoute(path) {
  for (const route of routes) {
    const match = matchPath(route.pattern)(path);
    if (match) return { component: route.component, params: match.params };
  }
}

Scenario 2: Syncing Filter State with URL

You have a data grid with filters that should be shareable via URL.

  • Best choice: query-string
  • Why? It easily converts your state object to a string and back.
// State sync
const filters = { status: 'open', sort: 'date' };
window.location.search = queryString.stringify(filters);

// On load
const state = queryString.parse(window.location.search);

Scenario 3: Hypermedia API Client

Your backend sends URLs with templates (e.g., HAL+JSON) that you must expand.

  • Best choice: uri-template
  • Why? It ensures you follow the same RFC 6570 rules as the server.
// API Client
const nextLink = response._links.next.href; // '/items{?page}'
const template = parse(nextLink);
const nextUrl = template.expand({ page: 2 });

Scenario 4: Simple URL Construction

You need to build URLs for an internal tool without complex standards.

  • Best choice: url-template
  • Why? Lightweight and easy to read for simple {placeholder} syntax.
// Internal Tool
const apiBase = '/api/v1';
const template = urlTemplate.parse(`${apiBase}/users/{userId}`);
const url = template.expand({ userId: 42 });

📊 Summary Table

Featurepath-to-regexpquery-stringuri-templateurl-template
Primary UsePath MatchingQuery ParsingURL ExpansionURL Expansion
RFC CompliantNoNoYes (RFC 6570)No
Handles QueryNoYesYes (in template)Yes (in template)
MaintenanceHighHighModerateLow/Moderate
Best ForRoutingState SyncHypermedia APIsSimple Construction

💡 Final Recommendation

For most frontend applications, you will need path-to-regexp and query-string together. They cover routing and state management, which are the most common URL-related tasks.

Use uri-template only if you are integrating with a backend that explicitly sends RFC 6570 templates (common in enterprise or hypermedia APIs).

Use url-template for lightweight internal tools where strict standards compliance is not required, but verify the specific package's maintenance status before committing.

Final Thought: Don't try to force one library to do another's job. path-to-regexp matches, query-string parses, and template libraries construct. Using the right tool for each layer keeps your URL logic clean and bug-free.

How to Choose: path-to-regexp vs query-string vs uri-template vs url-template

  • path-to-regexp:

    Choose path-to-regexp when you need to match URL paths against patterns, such as building a custom router or validating route structures. It is the industry standard for path matching in frameworks like Express and React Router. Use it if your primary goal is extracting parameters from an existing path string rather than constructing URLs.

  • query-string:

    Choose query-string when you need to reliably parse or stringify URL query parameters in the browser or Node.js. It handles edge cases like array formatting, encoding, and sorting better than the native URLSearchParams in many scenarios. It is the best fit for managing filter states, pagination, or any data passed via the query string.

  • uri-template:

    Choose uri-template if you are working with APIs that strictly follow RFC 6570 for URL templating. This is common in hypermedia-driven APIs (like HAL or JSON:API). Use it when you need standard-compliant expansion of variables within a URI template string, especially for interoperability with other systems.

  • url-template:

    Choose url-template if you need a lightweight, flexible way to fill placeholders in URLs without strict RFC 6570 compliance. It is suitable for internal tools or simpler use cases where you just need to replace {key} with values. However, verify its maintenance status, as it is less commonly updated than path-to-regexp or query-string.

README for path-to-regexp

Path-to-RegExp

Turn a path string such as /user/:name into a regular expression.

NPM version NPM downloads Build status Build coverage License

Installation

npm install path-to-regexp --save

Usage

const {
  match,
  pathToRegexp,
  compile,
  parse,
  stringify,
} = require("path-to-regexp");

Parameters

Parameters match arbitrary strings in a path by matching up to the end of the segment, or up to any proceeding tokens. They are defined by prefixing a colon to the parameter name (:foo). Parameter names can use any valid JavaScript identifier, or be double quoted to use other characters (:"param-name").

const fn = match("/:foo/:bar");

fn("/test/route");
//=> { path: '/test/route', params: { foo: 'test', bar: 'route' } }

Wildcard

Wildcard parameters match one or more characters across multiple segments. They are defined the same way as regular parameters, but are prefixed with an asterisk (*foo).

const fn = match("/*splat");

fn("/bar/baz");
//=> { path: '/bar/baz', params: { splat: [ 'bar', 'baz' ] } }

Optional

Braces can be used to define parts of the path that are optional.

const fn = match("/users{/:id}/delete");

fn("/users/delete");
//=> { path: '/users/delete', params: {} }

fn("/users/123/delete");
//=> { path: '/users/123/delete', params: { id: '123' } }

Match

The match function returns a function for matching strings against a path:

  • path String, TokenData object, or array of strings and TokenData objects.
  • options (optional) (Extends pathToRegexp options)
    • decode Function for decoding strings to params, or false to disable all processing. (default: decodeURIComponent)
const fn = match("/foo/:bar");

Please note: path-to-regexp is intended for ordered data (e.g. paths, hosts). It can not handle arbitrarily ordered data (e.g. query strings, URL fragments, JSON, etc).

PathToRegexp

The pathToRegexp function returns the regexp for matching strings against paths, and an array of keys for understanding the RegExp#exec matches.

  • path String, TokenData object, or array of strings and TokenData objects.
  • options (optional) (See parse for more options)
    • sensitive Regexp will be case sensitive. (default: false)
    • end Validate the match reaches the end of the string. (default: true)
    • delimiter The default delimiter for segments, e.g. [^/] for :named parameters. (default: '/')
    • trailing Allows optional trailing delimiter to match. (default: true)
const { regexp, keys } = pathToRegexp("/foo/:bar");

regexp.exec("/foo/123"); //=> ["/foo/123", "123"]

Compile ("Reverse" Path-To-RegExp)

The compile function will return a function for transforming parameters into a valid path:

  • path A string or TokenData object.
  • options (See parse for more options)
    • delimiter The default delimiter for segments, e.g. [^/] for :named parameters. (default: '/')
    • encode Function for encoding input strings for output into the path, or false to disable entirely. (default: encodeURIComponent)
const toPath = compile("/user/:id");

toPath({ id: "name" }); //=> "/user/name"
toPath({ id: "café" }); //=> "/user/caf%C3%A9"

const toPathRepeated = compile("/*segment");

toPathRepeated({ segment: ["foo"] }); //=> "/foo"
toPathRepeated({ segment: ["a", "b", "c"] }); //=> "/a/b/c"

// When disabling `encode`, you need to make sure inputs are encoded correctly. No arrays are accepted.
const toPathRaw = compile("/user/:id", { encode: false });

toPathRaw({ id: "%3A%2F" }); //=> "/user/%3A%2F"

Stringify

Transform a TokenData object to a Path-to-RegExp string.

  • data A TokenData object.
const data = {
  tokens: [
    { type: "text", value: "/" },
    { type: "param", name: "foo" },
  ],
};

const path = stringify(data); //=> "/:foo"

Developers

  • If you are rewriting paths with match and compile, consider using encode: false and decode: false to keep raw paths passed around.
  • To ensure matches work on paths containing characters usually encoded, such as emoji, consider using encodeurl for encodePath.

Parse

The parse function accepts a string and returns TokenData, which can be used with match and compile.

  • path A string.
  • options (optional)
    • encodePath A function for encoding input strings. (default: x => x, recommended: encodeurl)

Tokens

TokenData has two properties:

  • tokens A sequence of tokens, currently of types text, param, wildcard, or group.
  • originalPath The original path used with parse, shown in error messages to assist debugging.

Custom path

In some applications you may not be able to use the path-to-regexp syntax, but you still want to use this library for match and compile. For example:

import { match } from "path-to-regexp";

const tokens = [
  { type: "text", value: "/" },
  { type: "param", name: "foo" },
];
const originalPath = "/[foo]"; // To help debug error messages.
const path = { tokens, originalPath };
const fn = match(path);

fn("/test"); //=> { path: '/test', params: { foo: 'test' } }

Errors

An effort has been made to ensure ambiguous paths from previous releases throw an error. This means you might be seeing an error when things worked before.

Missing parameter name

Parameter names must be provided after : or *, for example /*path. They can be valid JavaScript identifiers (e.g. :myName) or JSON strings (:"my-name").

Unexpected ? or +

In past releases, ?, *, and + were used to denote optional or repeating parameters. As an alternative, try these:

  • For optional (?), use braces: /file{.:ext}.
  • For one or more (+), use a wildcard: /*path.
  • For zero or more (*), use both: /files{/*path}.

Unexpected (, ), [, ], etc.

Previous versions of Path-to-RegExp used these for RegExp features. This version no longer supports them so they've been reserved to avoid ambiguity. To match these characters literally, escape them with a backslash, e.g. "\\(".

Unterminated quote

Parameter names can be wrapped in double quote characters, and this error means you forgot to close the quote character. For example, :"foo.

Express <= 4.x

Path-To-RegExp breaks compatibility with Express <= 4.x in the following ways:

  • The wildcard * must have a name and matches the behavior of parameters :.
  • The optional character ? is no longer supported, use braces instead: /:file{.:ext}.
  • Regexp characters are not supported.
  • Some characters have been reserved to avoid confusion during upgrade (()[]?+!).
  • Parameter names now support valid JavaScript identifiers, or quoted like :"this".

License

MIT