Library / SDK
micromatch/picomatch avatar
micromatch/picomatch

picomatch turns a glob into a reusable matcher, with no dependencies

Blazing fast and accurate glob matcher written JavaScript, with no dependencies and full support for standard and extended Bash glob features, including braces, extglobs, POSIX brackets, and regular expressions. Used by GraphQL, Jest, Astro, Snowpack, Storybook, bulma, Serverless, fdir, Netlify, AWS Amplify, Revogrid, rollup, routify, open-wc, imba, ava, docusaurus, fast-glob, globby, chokidar, anymatch, cloudflare/miniflare, pts, and more than 5 million projects! Please follow picomatch's author: https://github.com/jonschlinkert

1,305 stars164 forksJavaScriptMIT

At a glance

What is it?
picomatch is an MIT licensed JavaScript glob matcher that compiles a pattern into a regular expression and returns a matcher function, supporting extglobs, braces, POSIX brackets and regex, with no dependencies. It suits build tooling and anything that matches paths in bulk, and it is a matcher rather than a file walker.
Who is it for?
Adopt picomatch if you need to match path strings against glob patterns quickly and without pulling in a dependency tree, especially in a build tool or a watcher where matching happens thousands of times. Do not adopt it expecting it to walk the filesystem, since picomatch only matches strings and does not find files.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 15 days ago.
What is it written in?
Mainly JavaScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The API is one function, and the matcher is meant to be reused

picomatch is a glob matcher written in JavaScript with no dependencies and full support for standard and extended Bash glob features, including braces, extglobs, POSIX brackets and regular expressions.

The main export is a function that takes a glob pattern and an options object and returns a function for matching strings:

js
const pm = require('picomatch');
const isMatch = pm('*.js');

console.log(isMatch('abcd')); //=> false
console.log(isMatch('a.js')); //=> true
console.log(isMatch('a.md')); //=> false
console.log(isMatch('a/b.js')); //=> false

That last line is the difference between a glob and a path match, and it is worth being explicit about. A pattern of *.js does not match a/b.js, because * does not cross a directory separator. The pattern that does is **/*.js, and getting that distinction right is most of what a glob implementation is.

The reason the API returns a function rather than a boolean is performance, and the README lists it as a separate point. Use the returned matcher function to speed up repeat matching, like when watching files. Compiling a glob to a regular expression is not free, and a file watcher that re-evaluates the same handful of patterns against every event should not pay that cost each time.

The design goal is stated as tiny API surface. The main export is a function that takes a glob pattern and returns a matcher function, and the whole list of named methods is picomatch, test, matchBase, isMatch, parse, scan, compileRe, makeRe and toRegex.

The last one, toRegex, is the escape hatch. The library is built by compiling globs to regular expressions, and giving you the regex means you can drop it into a system that wants a regular expression rather than a matcher.

The statics cover the one-shot cases without building a matcher

The named methods exist for the cases where building a matcher is overkill, and each one has a specific shape.

picomatch.test takes the input string and a regex and returns an object with matching info, which is what the main function uses internally. The README shows it returning the isMatch flag along with the match array and the output:

js
console.log(picomatch.test('foo/bar', /^(?:([^/]*?)\/([^/]*?))$/));
// { isMatch: true, match: [ 'foo/', 'foo', 'bar' ], output: 'foo/bar' }

picomatch.isMatch returns true if any of the given patterns match the string, taking a string or an array of patterns. The example shows the difference between an array and a single pattern clearly:

js
console.log(picomatch.isMatch('a.a', ['b.*', '*.a'])); //=> true
console.log(picomatch.isMatch('a.a', 'b.*')); //=> false

picomatch.matchBase matches the basename of a filepath rather than the whole path, so picomatch.matchBase('foo/bar.js', '*.js') is true. That is the option you want for a pattern like *.js applied to a nested file, and it is the sort of thing that otherwise gets implemented by hand in a build script.

picomatch.parse parses a glob pattern to create the source string for a regular expression, returning an object with useful properties and an output to be used as a regex source string. picomatch.scan separates the pattern into segments and returns a prefix, the input, a start offset and the rest. The scan example is revealing:

js
const result = picomatch.scan('!./foo/*.js');
console.log(result);
{ prefix: '!./',
  input: '!./foo/*.js',
  start

The prefix is the negation and the leading path, and separating it out is what lets the library handle a negated pattern as a set operation rather than as a literal to match against. The rest of the API, compileRe, makeRe and toRegex, are the regular expression layer underneath, and they are documented as such.

The overall shape is the important thing. There is one factory, a handful of statics, and the regex layer if you need it. A library you can hold in your head is one you will actually use instead of reaching for a heavier tool.

Extglobs, braces and POSIX brackets are what separate it from a naive matcher

The globbing features section is where picomatch does the work that distinguishes it from a simple wildcard matcher.

Basic globbing covers wildcards with * and ?, globstars with ** for nested directories, and escaping of special characters with a backslash or quotes. Advanced globbing adds extglobs, braces and POSIX brackets.

The example in the API section shows an extglob at work:

js
const isMatch = picomatch('*.!(*a)');
console.log(isMatch('a.a')); //=> false
console.log(isMatch('a.b')); //=> true

That pattern matches any file that does not end in a, and it is a Bash extglob rather than a JavaScript regular expression feature. The examples/ directory backs up the breadth, with separate files for extglob.js, extglob-negated.js, makeRe.js, match.js, regex-quantifier.js and scan.js, plus option-specific examples like option-expandRange.js, option-ignore.js, option-onIgnore.js, option-onMatch.js and option-onResult.js.

The regex-quantifier example is the one that matters for correctness. A naive glob to regex conversion turns * into .* and leaves it there, and that matches a path separator too, so *.js would match a/b.js. picomatch handles that by translating a quantifier into a form that does not cross a directory boundary, which is why the library has an example dedicated to that single transformation.

The option examples tell you what the options object is for. option-expandRange is about brace expansion of ranges, option-ignore and option-onIgnore are about what happens when a pattern has no match, and option-onMatch and option-onResult are hooks for reacting to matches as they are found.

The description of the options in the README also names a set of features that are easy to miss: picomatch supports regular expressions directly, so a pattern can be a RegExp rather than a string, and it supports matching special characters as literals, so a file literally named [draft] can be matched rather than being interpreted as a character class.

picomatch/posix exists for environments without automatic OS detection

There is one entry point that deserves its own note, because it tells you something about how the library thinks about paths.

The main entry point does automatic OS detection, so the same pattern behaves differently on Windows and on POSIX. For environments without node.js, picomatch/posix provides a dependency-free matcher without automatic OS detection:

js
const picomatch = require('picomatch/posix');
// the same API, defaulting to posix paths
const isMatch = picomatch('a/*');
console.log(isMatch('a\\b')); //=> false
console.log(isMatch('a/b')); //=> true

The same API with posix path defaults. And you can still configure the matcher function to accept Windows paths if you need to:

js
const isMatch = picomatch('a/*', { options: windows });
console.log(isMatch('a\\b')); //=> true
console.log(isMatch('a/b')); //=> true

So the choice is between a matcher that guesses based on the host and one that has a fixed, explicit convention. The second is what you want in a browser bundle, in a worker, or in any context where there is no operating system to detect, and it is also what you want when a build tool has to produce identical results on every platform, which is a real problem in cross-platform CI.

The package.json confirms the layout. The main field is index.js, and the files list includes index.js, posix.js and lib. There is a top-level posix.js in the repository root for that reason, alongside index.js and the lib/ directory. The engines field requires Node.js 12 or newer, and sideEffects is set to false, which tells a bundler that importing the module has no side effects and can be tree-shaken.

The install cost is one package, and so is the runtime cost

Installation is a single npm command:

sh
npm install --save picomatch

There is no dependency tree, and the reason the list of projects using it is so long is a direct consequence. The repository description names GraphQL, Jest, Astro, Snowpack, Storybook, bulma, Serverless, fdir, Netlify, AWS Amplify, Revogrid, rollup, routify, open-wc, imba, ava, docusaurus, fast-glob, globby, chokidar, anymatch and cloudflare/miniflare, and says more than 5 million projects.

That list is a map of the JavaScript toolchain, and it is dominated by build tools and watchers. Those are exactly the programs that match paths in bulk, often against the same handful of patterns, which is the case picomatch's returned-matcher design is optimised for.

The load-time claim is the other headline, and it is stated as loads in about 2ms, which the README compares to several times faster than a single frame of an HD movie at 60fps. The comparison is a bit theatrical, but the underlying point stands: a matcher that is imported by every build tool in the tree has a load cost that is paid on every process start, and a dependency-free library with a small surface area keeps that cost near zero.

The build tooling around the project is minimal and conventional. Lint is eslint with a cache, the test script runs lint and then mocha with the dot reporter, and the coverage path is nyc around the mocha run. The devDependencies are eslint, fill-range, gulp-format-md, mocha and nyc, and the README says the library is well tested with thousands of unit tests.

The coverage reporter configuration lists html, lcov, text-summary and cobertura, and there is a bench/ directory for the benchmarks the README references. The README also has a library comparison section pointing at the other options, which is the honest thing for a project in this space to publish.

MIT licensed, maintained under the micromatch banner, with a single author

The licence is MIT, and the package.json records the license field as MIT, with the LICENSE file in the repository as the authoritative text. This is a licensing statement rather than legal advice.

The release record is steady. The recent versions are 4.0.7 on 2026-08-24, 4.0.6 on 2026-08-24 and 4.0.5 on 2026-07-02, and the last push was on 2026-10-08 with the repository not archived. Two releases on the same day in August suggests a maintenance release followed by a fix, which is what you would expect from a library that has to keep its glob semantics aligned with the tools that use it.

The authorship is worth noting, because it is unusual for a library this widely used. The package.json lists Jon Schlinkert as the author, and the repository description carries an instruction to follow picomatch's author, linking to the same profile. The default branch is master, and the repository lives under the micromatch organisation rather than a personal account.

That is the second generation of a project with a well-known lineage. picomatch is the smaller, dependency-free one. The topics list carries both names alongside minimatch and multimatch, which are the other names you will meet in this space, and the repository README's library comparison section exists precisely to explain when to pick which.

The package.json also has a sideEffects: false declaration, an engines field of node >=12, and a keywords list of glob, match and picomatch. The repository has a renovate.json for dependency updates, a.verb.md for the documentation generator, an.eslintrc.json, and an examples/ directory with a set of runnable examples. The examples being committed as files rather than only as README snippets is a small sign of care, since each one is a runnable artefact a contributor can extend.

Editorial conclusion

Adopt picomatch if you need to match path strings against glob patterns quickly and without pulling in a dependency tree, especially in a build tool or a watcher where matching happens thousands of times. Do not adopt it expecting it to walk the filesystem, since picomatch only matches strings and does not find files. Before you integrate, read the library comparison section, since minimatch and micromatch overlap with it heavily, and check the posix entry point if you run outside Node.js, because picomatch/posix avoids automatic OS detection.

Frequently asked questions

How do I install and use picomatch?

Run npm install --save picomatch, then require it and call it with a pattern. The main export takes a glob pattern and an options object and returns a matching function, so const isMatch = pm('*.js') gives you a function you can call against many strings, which is faster than recompiling the pattern each time.

Does picomatch match across directory separators?

Not with a plain asterisk. In the README example, a pattern of *.js does not match a/b.js, because * does not cross a directory separator. The globstar ** is what matches nested directories, and the library includes a regex-quantifier example showing how the quantifier is translated so it does not cross a boundary.

What is the difference between picomatch and picomatch/posix?

The main entry point does automatic OS detection, while picomatch/posix provides a dependency-free matcher with the same API that defaults to posix paths without detecting the OS. That is the one to use in a browser or worker, and the Windows behaviour can still be enabled through the options object.

Which glob features does picomatch support?

Wildcards with * and ?, globstars with **, advanced globbing with extglobs, braces and POSIX brackets, regular expressions, and escaping of special characters with a backslash or quotes. The examples directory includes separate files for extglobs, negated extglobs, regex quantifiers, brace range expansion and the ignore and onMatch options.

Is picomatch actively maintained and under what licence?

The repository is not archived, the last push was on 2026-10-08, and the recent releases are 4.0.7 and 4.0.6 on 2026-08-24 and 4.0.5 on 2026-07-02. The licence is MIT, with the LICENSE file in the repository as the authoritative text, and the package is authored by Jon Schlinkert.

Official sources

  1. License: MIT
  2. micromatch/picomatch on GitHub
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/micromatch-picomatch.svg)](https://hysenlabs.com/projects/micromatch-picomatch)

Was this useful?