filters-query-params
TypeScript icon, indicating that this package has built-in type declarations

1.0.1 • Public • Published

🔍 filters-query-params

Type-safe URL query parameter management for React, Next.js, and vanilla TypeScript with Zod validation.

npm version TypeScript MIT License

✨ Features

  • 🛡️ Type-safe with Zod schema validation
  • 🔄 Smart type coercion (string → number/boolean/date)
  • 🧹 Data cleaning (drop empty values, trim strings, strip unknown keys)
  • 📦 Multiple array formats (repeat, comma, JSON)
  • 📅 Date handling with ISO encoding/decoding
  • Debouncing with 300ms default delay
  • ⚛️ React hooks for Next.js (App/Pages Router) and React Router
  • 🚀 SSR-ready with server-side helpers
  • 📝 ESM + CJS with full TypeScript definitions

📦 Installation

npm install filters-query-params zod

🚀 Quick Start

1. Define Your Schema

import { z } from "zod";

const filtersSchema = z.object({
  search: z.string().optional(),
  category: z.string().optional(),
  priceMin: z.number().int().min(0).optional(),
  priceMax: z.number().int().min(0).optional(),
  inStock: z.boolean().optional(),
  tags: z.array(z.string()).optional(),
  createdAfter: z.date().optional(),
});

2. Core Usage

Parse Query Strings

import { parseQuery } from "filters-query-params";

const filters = parseQuery(filtersSchema, "?search=laptop&priceMin=100", {
  coerceTypes: true, // Convert strings to proper types
  dropEmpty: true, // Remove empty values
  arrayFormat: "repeat", // Handle ?tags=a&tags=b
});
// Result: { search: "laptop", priceMin: 100 }

Build URLs

import { buildUrl, buildQuery } from "filters-query-params";

const url = buildUrl(
  "/products",
  filtersSchema,
  {
    search: "laptop",
    tags: ["gaming", "portable"],
  },
  { arrayFormat: "repeat" }
);
// Result: "/products?search=laptop&tags=gaming&tags=portable"

const queryParams = buildQuery(filtersSchema, { search: "laptop" });
// Result: URLSearchParams object

3. React Hooks

Next.js App Router

"use client";
import { useNextAppFilters } from "filters-query-params";

export default function ProductsPage() {
  const { filters, setFilters, reset, isLoading } =
    useNextAppFilters(filtersSchema);

  return (
    <div>
      <input
        value={filters.search || ""}
        onChange={(e) => setFilters({ search: e.target.value })}
        placeholder="Search..."
      />
      <button onClick={() => setFilters({ inStock: true })}>
        In Stock Only
      </button>
      <button onClick={() => reset()}>Clear All</button>
    </div>
  );
}

Next.js Pages Router

import { useNextPagesFilters } from "filters-query-params";

export default function ProductsPage() {
  const { filters, setFilters, reset } = useNextPagesFilters(filtersSchema);
  // Same usage as App Router
}

React Router

import { useReactRouterFilters } from "filters-query-params";

export function ProductsPage() {
  const { filters, setFilters, reset } = useReactRouterFilters(filtersSchema);
  // Same usage as Next.js
}

4. Utility Functions

import { cleanObject, mergeFilters, resetFilters } from "filters-query-params";

// Clean data
const clean = cleanObject(
  { name: " John ", age: "", active: true },
  { trimStrings: true, dropEmpty: true }
);
// Result: { name: "John", active: true }

// Merge filters
const merged = mergeFilters({ search: "old" }, { category: "new" });
// Result: { search: "old", category: "new" }

// Reset to defaults
const reset = resetFilters(filtersSchema, { category: "electronics" });
// Result: { category: "electronics" }

5. Server-Side Rendering

import { getFiltersFromUrl, getFiltersFromSearch } from "filters-query-params";

// From full URL
const filters = getFiltersFromUrl(filtersSchema, req.url, {
  coerceTypes: true,
});

// From search params only
const filters2 = getFiltersFromSearch(filtersSchema, "?search=laptop&page=1");

📖 API Reference

Core Functions

Function Purpose Example
parseQuery(schema, input, options?) Parse query string to typed object parseQuery(schema, "?q=test")
buildQuery(schema, filters, options?) Build URLSearchParams from object buildQuery(schema, { q: "test" })
buildUrl(baseUrl, schema, filters, options?) Build complete URL with params buildUrl("/search", schema, filters)
cleanObject(obj, options?) Clean object (trim, drop empty) cleanObject({ name: " John " })
mergeFilters(current, next, options?) Merge filter objects mergeFilters(old, new)
resetFilters(schema, defaults?) Reset to default values resetFilters(schema, { page: 1 })

React Hooks

Hook Framework Purpose
useNextAppFilters(schema, options?) Next.js App Router Manage filters with URL sync
useNextPagesFilters(schema, options?) Next.js Pages Router Manage filters with URL sync
useReactRouterFilters(schema, options?) React Router Manage filters with URL sync

Debouncing Functions

Function Purpose Example
debounce(fn, delay?) Debounce any function debounce(searchFn, 300)
createDebouncedParseQuery(schema, delay?) Debounced query parsing createDebouncedParseQuery(schema)
createDebouncedBuildUrl(schema, delay?) Debounced URL building createDebouncedBuildUrl(schema)
createDebouncedBuildQuery(schema, delay?) Debounced query building createDebouncedBuildQuery(schema)

SSR Helpers

Function Purpose Example
getFiltersFromUrl(schema, url, options?) Extract filters from full URL getFiltersFromUrl(schema, req.url)
getFiltersFromSearch(schema, search, options?) Extract filters from search params getFiltersFromSearch(schema, "?q=test")

⚙️ Configuration

Array Formats

Format URL Example Use Case
"repeat" ?tags=a&tags=b Most compatible, default
"comma" ?tags=a,b Shorter URLs
"json" ?tags=["a","b"] Complex data structures

Options

ParseOptions & BuildOptions

{
  stripUnknown?: boolean;     // Remove unknown keys
  trimStrings?: boolean;      // Trim whitespace
  dropEmpty?: boolean;        // Remove empty values
  coerceTypes?: boolean;      // Convert string types (parse only)
  arrayFormat?: ArrayFormat;  // Array serialization format
  arrayKeyFormat?: Record<string, ArrayFormat>; // Per-key formats
  encodeDate?: boolean;       // Encode dates as ISO strings (build only)
}

� Advanced Usage

Debouncing

import { debounce, createDebouncedParseQuery } from "filters-query-params";

// Debounce any function
const debouncedSearch = debounce(async (query: string) => {
  return fetch(`/api/search?q=${query}`);
}, 300);

// Debounced query parsing
const debouncedParseQuery = createDebouncedParseQuery(filtersSchema, 300);
debouncedParseQuery("?search=laptop");
debouncedParseQuery("?search=macbook"); // Only this executes after 300ms

// Cancel or flush immediately
debouncedParseQuery.cancel();
const immediate = debouncedParseQuery.flush("?search=immediate");

Date Handling

const url = buildUrl(
  "/events",
  schema,
  {
    startDate: new Date("2024-01-01"),
    endDate: new Date("2024-12-31"),
  },
  { encodeDate: true }
);

// Parses dates back from ISO strings
const filters = parseQuery(schema, url, { coerceTypes: true });
console.log(filters.startDate instanceof Date); // true

Data Cleaning

const filters = parseQuery(schema, "?search=  hello  &category=&priceMin=0", {
  trimStrings: true, // "  hello  " → "hello"
  dropEmpty: true, // Remove category=""
  coerceTypes: true, // "0" → 0
});
// Result: { search: "hello", priceMin: 0 }

Mixed Array Formats

const { filters } = useNextAppFilters(schema, {
  build: {
    arrayFormat: "repeat", // default for most arrays
    arrayKeyFormat: {
      tags: "comma", // use comma for tags specifically
      categories: "json", // use JSON for categories
    },
  },
});

� Contributing

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

📄 License

MIT License - see LICENSE for details.

Copyright © 2025 Nauman Majeed

Package Sidebar

Install

npm i filters-query-params

Weekly Downloads

3

Version

1.0.1

License

MIT

Unpacked Size

97.7 kB

Total Files

9

Last publish

Collaborators

  • inaumanmajeed