npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

build-url-ts

v6.3.0

Published

A small library that builds a URL given its components

Readme

build-url-ts

Build Status NPM version Downloads Bundle Size UNPKG License: MIT

A small, fast, zero-dependency library for building URLs with a fluent API. Fully typed for TypeScript and runs anywhere JavaScript does — Node.js, Bun, Deno, edge runtimes, and all modern browsers.

Edit build-url-ts-demo

Features

  • 🚀 Small & Fast - Minimal footprint with zero dependencies (~1.3 KB min+gzip)
  • 📦 TypeScript Support - Full TypeScript definitions for both ESM and CJS
  • 🔀 Dual Package - First-class ESM + CommonJS entry points, plus a UMD bundle for CDNs
  • 🌳 Tree-shakeable - Side-effect-free ESM; import only what you use
  • 🌐 Universal - Node.js, Bun, Deno, edge runtimes, and all modern browsers
  • 🔧 Flexible - Multiple ways to handle array query parameters
  • Clean API - Simple and intuitive interface
  • 🛡️ Safe - Properly encodes URLs and handles edge cases
  • 🧪 Well Tested - Comprehensive test coverage with 137+ test cases
  • 🔄 Smart Merging - Automatically merges with existing query parameters

Installation

# bun
bun add build-url-ts

# npm
npm install build-url-ts

# yarn
yarn add build-url-ts

# pnpm
pnpm add build-url-ts

Quick Start

import { buildUrl } from 'build-url-ts';

const url = buildUrl('https://api.example.com', {
  path: 'users/123',
  queryParams: {
    tab: 'profile',
    limit: 10
  }
});
// Result: https://api.example.com/users/123?tab=profile&limit=10

Module Formats & Platforms

The package ships ESM, CommonJS, and a minified UMD bundle, with separate type declarations for each module system, so it works the same everywhere.

ESM — bundlers, Node, Bun

import { buildUrl } from 'build-url-ts';

CommonJS — require()

const { buildUrl } = require('build-url-ts');

Deno

import { buildUrl } from 'npm:build-url-ts';

Browser via CDN (<script>)

The UMD build exposes a global buildUrl:

<script src="https://unpkg.com/build-url-ts"></script>
<script>
  buildUrl.buildUrl('https://example.com', { path: 'about' });
</script>

For production, pin a version and add Subresource Integrity, e.g. <script src="https://unpkg.com/[email protected]/dist/index.umd.min.js" integrity="sha384-…" crossorigin="anonymous"></script>.

Or as an ES module, no bundler required:

<script type="module">
  import { buildUrl } from 'https://esm.sh/build-url-ts';
  console.log(buildUrl('https://example.com', { path: 'about' }));
</script>

Tree-shaking

The library is published as side-effect-free ESM ("sideEffects": false), so bundlers drop everything you don't import. Pull in a single helper and only that helper ends up in your bundle:

import { buildQueryString } from 'build-url-ts';
// buildUrl, appendPath, buildHash are tree-shaken away

API Reference

buildUrl(baseUrl?, options?)

Builds a complete URL from components.

Parameters

  • baseUrl (optional): string | null - The base URL
  • options (optional): IBuildUrlOptions - URL building options

Options

interface IBuildUrlOptions {
  path?: string | number;           // Single path segment
  paths?: (string | number)[];      // Multiple path segments, appended in order
  queryParams?: IQueryParams;       // Query parameters object
  hash?: string | number;           // Hash/fragment identifier
  lowerCase?: boolean;              // Convert to lowercase
  disableCSV?: boolean | IDisableCsvType; // Array handling
}

Usage Examples

Basic URL Building

import { buildUrl } from 'build-url-ts';

// Simple URL with path
buildUrl('https://example.com', {
  path: 'about'
});
// → https://example.com/about

// Multiple path segments (normalized and joined); `path` still works for one
buildUrl('https://example.com', {
  paths: ['about', '/my/', '/cat']
});
// → https://example.com/about/my/cat

// With query parameters
buildUrl('https://example.com', {
  path: 'search',
  queryParams: {
    q: 'typescript',
    category: 'tutorials'
  }
});
// → https://example.com/search?q=typescript&category=tutorials

// With hash
buildUrl('https://example.com', {
  path: 'docs',
  hash: 'installation'
});
// → https://example.com/docs#installation

// All combined
buildUrl('https://api.example.com', {
  path: 'v1/users',
  queryParams: {
    role: 'admin',
    active: true
  },
  hash: 'summary'
});
// → https://api.example.com/v1/users?role=admin&active=true#summary

Working with Arrays

// Default: Arrays as comma-separated values
buildUrl('https://api.example.com', {
  queryParams: {
    ids: [1, 2, 3]
  }
});
// → https://api.example.com?ids=1,2,3

// Arrays as repeated parameters
buildUrl('https://api.example.com', {
  queryParams: {
    id: [1, 2, 3]
  },
  disableCSV: true
});
// → https://api.example.com?id=1&id=2&id=3

// Arrays with bracket notation
buildUrl('https://api.example.com', {
  queryParams: {
    id: [1, 2, 3]
  },
  disableCSV: 'array'
});
// → https://api.example.com?id[]=1&id[]=2&id[]=3

// Arrays with indexed notation (ascending)
buildUrl('https://api.example.com', {
  queryParams: {
    id: [1, 2, 3]
  },
  disableCSV: 'order_asc'
});
// → https://api.example.com?id[0]=1&id[1]=2&id[2]=3

// Arrays with indexed notation (descending)
buildUrl('https://api.example.com', {
  queryParams: {
    id: [1, 2, 3]
  },
  disableCSV: 'order_desc'
});
// → https://api.example.com?id[2]=1&id[1]=2&id[0]=3

Case Transformation

// Convert to lowercase
buildUrl('https://example.com', {
  path: 'About',
  hash: 'Contact',
  queryParams: {
    Filter: 'NEW'
  },
  lowerCase: true
});
// → https://example.com/about?filter=new#contact

Building Partial URLs

// Query string only
buildUrl(null, {
  queryParams: {
    page: 1,
    limit: 20
  }
});
// → ?page=1&limit=20

// Path only
buildUrl(null, {
  path: 'users/profile'
});
// → /users/profile

// Hash only
buildUrl(null, {
  hash: 'top'
});
// → #top

// Using options as first parameter
buildUrl({
  path: 'api/v2',
  queryParams: {
    format: 'json'
  }
});
// → /api/v2?format=json

Handling Special Values

// Null values become empty strings
buildUrl('https://api.example.com', {
  queryParams: {
    name: 'John',
    age: null
  }
});
// → https://api.example.com?name=John&age=

// Undefined values are omitted
buildUrl('https://api.example.com', {
  queryParams: {
    name: 'John',
    age: undefined
  }
});
// → https://api.example.com?name=John

// Number values
buildUrl('https://api.example.com', {
  path: 404,
  queryParams: {
    code: 0,
    retry: 3
  }
});
// → https://api.example.com/404?code=0&retry=3

// Boolean values
buildUrl('https://api.example.com', {
  queryParams: {
    active: true,
    deleted: false
  }
});
// → https://api.example.com?active=true&deleted=false

// Date objects
const date = new Date('2024-01-01T00:00:00Z');
buildUrl('https://api.example.com', {
  queryParams: {
    created: date
  }
});
// → https://api.example.com?created=Mon%20Jan%2001%202024...

// Nested objects (automatically stringified)
buildUrl('https://api.example.com', {
  queryParams: {
    filter: { status: 'active', role: 'admin' }
  }
});
// → https://api.example.com?filter=%7B%22status%22%3A%22active%22%2C%22role%22%3A%22admin%22%7D

Advanced Usage

Using Individual Functions

The library also exports individual functions for more granular control:

import { 
  buildQueryString, 
  appendPath, 
  buildHash 
} from 'build-url-ts';

// Build query string only
const qs = buildQueryString({
  search: 'typescript',
  limit: 10
});
// → ?search=typescript&limit=10

// Append path to URL
const urlWithPath = appendPath('users/123', 'https://api.example.com');
// → https://api.example.com/users/123

// Build hash fragment
const hash = buildHash('section-2');
// → #section-2

TypeScript Types

import type { 
  IQueryParams, 
  IBuildUrlOptions,
  IDisableCsvType 
} from 'build-url-ts';

// Custom query params type
interface MyParams extends IQueryParams {
  userId: number;
  tags?: string[];
  active?: boolean;
}

const options: IBuildUrlOptions = {
  path: 'api/users',
  queryParams: {
    userId: 123,
    tags: ['admin', 'verified']
  } as MyParams
};

URL Encoding

All values are properly encoded for URLs:

buildUrl('https://example.com', {
  queryParams: {
    name: 'John Doe',
    email: '[email protected]',
    message: 'Hello & goodbye!',
    unicode: '你好世界'
  }
});
// → https://example.com?name=John%20Doe&email=john%40example.com&message=Hello%20%26%20goodbye!&unicode=%E4%BD%A0%E5%A5%BD%E4%B8%96%E7%95%8C

Edge Cases

The library handles various edge cases gracefully:

// Empty or missing base URL
buildUrl('', { path: 'api' }); // → /api
buildUrl(null, { path: 'api' }); // → /api
buildUrl(undefined, { path: 'api' }); // → /api

// Trailing slashes
buildUrl('https://example.com/', { path: '/users' }); 
// → https://example.com/users (no double slash)

// Empty values
buildUrl('https://example.com', {
  path: '',      // ignored
  hash: '',      // ignored
  queryParams: {
    empty: '',   // included as empty
    zero: 0,     // included as "0"
    false: false // included as "false"
  }
});
// → https://example.com?empty=&zero=0&false=false

// URLs with existing query parameters (automatic merging)
buildUrl('https://example.com?existing=param', {
  queryParams: {
    new: 'value'
  }
});
// → https://example.com?existing=param&new=value

// URLs with ports and authentication
buildUrl('http://user:pass@localhost:3000', {
  path: 'api/secure'
});
// → http://user:pass@localhost:3000/api/secure

// Special protocols
buildUrl('file:///home/user/data', {
  queryParams: { version: 2 }
});
// → file:///home/user/data?version=2

// Internationalized domain names and emoji
buildUrl('https://例え.jp', {
  queryParams: {
    search: '🔍',
    text: '你好'
  }
});
// → https://例え.jp?search=%F0%9F%94%8D&text=%E4%BD%A0%E5%A5%BD

// Empty arrays are omitted
buildUrl('https://api.example.com', {
  queryParams: {
    ids: [],
    name: 'test'
  }
});
// → https://api.example.com?name=test

// Arrays with null/undefined values
buildUrl('https://api.example.com', {
  queryParams: {
    items: ['one', null, undefined, 'four']
  },
  disableCSV: true
});
// → https://api.example.com?items=one&items=&items=four
// (undefined values are filtered out)

Migration Guide

From build-url

This library is a TypeScript fork of the original build-url package with improvements:

// Before (build-url)
var buildUrl = require('build-url');

// After (build-url-ts)
import { buildUrl } from 'build-url-ts';

The API remains fully compatible, so you can simply replace the import.

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add some amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

Development

This project uses Bun as its package manager, test runner, and script runner.

# Install dependencies
bun install

# Run tests
bun test

# Run tests in watch mode
bun run test:watch

# Run tests with coverage
bun run test:coverage

# Build the library (CJS + ESM + type declarations)
bun run build

# Lint and format (Biome)
bun run lint
bun run check

# Type checking
bun run typecheck

Test Coverage

The library has comprehensive test coverage with 137+ test cases covering:

  • Basic URL building scenarios
  • Various array handling modes
  • Special characters and encoding
  • Edge cases and error handling
  • Protocol support (http, https, file, ftp, etc.)
  • Internationalization and emoji support
  • Query parameter merging
  • Date and object serialization

License

This is licensed under MIT License. See details

Acknowledgments

This is a TypeScript enhancement of the original build-url library by Steve Rydz.