match-sorter
Simple, deterministic best-match sorting and filtering for arrays in JavaScript and TypeScript.
Repository Health
Technical Analysis
match-sorter is a small, dependency-light TypeScript library that filters and ranks a list of items against a search value using a sensible, human-friendly ordering: case-sensitive equals first, then equals, starts-with, word-starts-with, contains, acronym, and finally a fuzzy in-order character match. Instead of a black-box relevance score from a fuzzy-search algorithm, it produces the ordering a person would expect when they type into a filter box.
It is commonly reached for behind autocomplete inputs, command palettes, and table/list filtering UIs where the list of candidate strings or objects lives entirely in memory (as opposed to a server-side search index). The library accepts plain strings or objects with configurable keys (including dot-notation nested paths, wildcard array paths, and per-key thresholds/min/max ranking overrides), and exposes both a simple matchSorter() that returns filtered/sorted items and a matchSorterWithRankInfo() that also surfaces the rank metadata used to produce that order.
What You Get
- matchSorter() - filters and sorts an array of strings or objects against a query value in one call, returning only items that meet the ranking threshold, in best-match-first order
- matchSorterWithRankInfo() - the same ranking behavior but returns each matched item alongside its rank, keyIndex, and the matched keyedValue for building custom UI (e.g. highlighting the matched key)
- Configurable
keys- rank objects by one or more properties, including dot-notation nested paths (name.first), wildcard array paths (aliases.*.name.first), and function-based value getters for interop with structures like Immutable.js - Per-key thresholds and min/max ranking overrides - pin a specific key to only match at a stricter threshold, or clamp its contribution with
minRanking/maxRankingso one field can’t overrank or underrank the result - Exported
rankingsconstants - the same threshold values (CASE_SENSITIVE_EQUALthroughNO_MATCH) used internally are exported so consumers can pass a customthresholdor compare ranks themselves - Pluggable
baseSortandsorter- override the tie-breaking comparator for same-rank items, or replace the sorting step entirely while keeping the ranking computation
Common Use Cases
- Filtering a client-side autocomplete/typeahead dropdown as the user types
- Powering a command palette (like a VS Code-style
Cmd+Kmenu) where commands must surface in an intuitive order - Filtering rows of a data table or list against a free-text search box without a backend search index
- Ranking select/combobox options (e.g. a country or tag picker) so the closest match appears first
Under The Hood
Architecture
The entire library lives in a single module (src/index.ts) organized as a small pipeline rather than a class hierarchy: the two public entry points, matchSorter() and matchSorterWithRankInfo(), both delegate to a shared getRankedItems() that reduces the input array through getHighestRanking() for each item, which in turn calls getMatchRanking() per candidate key value and falls through an ordered chain of comparisons (exact, case-insensitive, starts-with, word-starts-with, contains, acronym) before handing off to getClosenessRanking() for the fuzzy in-order character-match score. Key resolution (getItemValues/getNestedValues/getAllValuesToRank) is a separate concern from ranking, so nested/wildcard object paths are flattened to plain string candidates before the same ranking logic runs on them. Because ranking and sorting are pure functions over immutable inputs with no shared mutable state, the core abstraction (the rankings hierarchy) could change without touching key-resolution or sorting code, and vice versa.
Tech Stack
The package is authored entirely in TypeScript and ships pre-built dist/match-sorter.cjs.js, dist/match-sorter.esm.js, and dist/index.d.ts outputs, built via kcd-scripts build (Kent C. Dodds’ shared Babel/Rollup-based build tooling). Runtime dependencies are deliberately minimal: @babel/runtime (for compiled helper output) and remove-accents (for diacritic-insensitive matching). Linting, type-checking, and testing all run through the same kcd-scripts toolchain (lint, typecheck, test), and CI is wired up via a single GitHub Actions workflow (.github/workflows/validate.yml) that runs the validate script on every push/PR.
Code Quality
Tests are extensive relative to the implementation size: src/__tests__/index.ts runs to roughly 750 lines against a ~570-line implementation, covering the ranking hierarchy, nested/wildcard key paths, threshold and min/max ranking overrides, custom baseSort/sorter functions, and diacritic handling with concrete input/output fixtures. Types are used throughout rather than bolted on afterward (RankingInfo, KeyOption, MatchSorterOptions are all exported), function names map directly to the ranking concepts described in the README, and the lint/typecheck/test scripts are enforced together via the validate script that CI runs on every change.
API Design
The public surface is intentionally small: matchSorter(list, query) works with zero configuration on a plain array of strings, and the same call accepts an optional options.keys array to rank objects (including dot-notation and wildcard paths) without changing the return shape. Escape hatches are opt-in rather than required — threshold, keepDiacritics, baseSort, and sorter are all optional overrides layered on top of sensible defaults, and the exported rankings constants let consumers reference the same threshold values the library uses internally instead of guessing at magic numbers. Getting from “I have an array” to “a correctly filtered and ordered array” requires exactly one function call in the common case.
Used by 10 apps in this directory
CodeSandbox
Code Editors · Developer Tools
Instantly ready browser-based IDE that runs full npm dependency resolution and transpilation entirely client-side, with no server needed.
highlight.io
Developer Tools · Analytics · Monitoring
Open-source full-stack monitoring that unifies session replay, error tracking, logging, and distributed tracing so you can stop context-switching between tools.
KeystoneJS
CMS · Developer Tools
The superpowered headless CMS for developers built with GraphQL and React
LibreChat
Developer Tools · AI Assistants
Unite every major AI model in one self-hosted chat platform with agents, code execution, MCP tools, and enterprise authentication.
medusa
Ecommerce
The most flexible open-source commerce platform — build B2C, B2B, and marketplace applications with modular, composable commerce primitives.
overleaf
Collaboration · Productivity
Open-source, real-time collaborative LaTeX editor with sandboxed compilation and full TeXLive support for self-hosted academic and research teams.
Rill
Analytics · Data Engineering
The fastest BI tool for humans and agents — define metrics, models, and dashboards as code and query them instantly on ClickHouse or DuckDB.
Rowy
Low Code Platforms · Databases
Manage Firestore data in an Airtable-like spreadsheet UI and build Cloud Functions workflows directly in your browser—on your own GCP project.
Trigger.dev
Automation · AI Development · Developer Tools
Build and deploy fully-managed AI agents and background jobs in TypeScript — with no timeouts, durable retries, real-time observability, and elastic scaling built in.