globby: glob matching that reads like it was written for people
User-friendly glob matching
At a glance
- What is it?
- A thin, opinionated layer over fast-glob that adds negation, directory expansion, gitignore support and a promise API, packaged by Sindre Sorhus.
- Who is it for?
- globby earns its place by handling the three things raw glob libraries leave to you: turning a directory into a recursive pattern, letting a negated pattern subtract from the set, and honouring the ignore files a repository already declares. The gitignore path is the part worth understanding rather than just switching on, since it reads files before traversal and hands the matcher patterns for directories it can prove are ignored.
- 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 49 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 September 24, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The three calls you actually need
The whole library is three entry points over one asynchronous core. `globby(patterns, options?)` returns a `Promise<string[]>` of matching paths, `globbySync` returns the same array without a promise, and `globbyStream` returns a `stream.Readable`.
The readme's opening example is the clearest statement of what the wrapper is for. Given a directory containing `unicorn`, `cake` and `rainbow`, a single call returns everything except one entry:
import {globby} from 'globby';
const paths = await globby(['*', '!cake']);
console.log(paths);
//=> ['unicorn', 'rainbow']A raw glob library would give you `*` and leave the subtraction to you. That is the core of the value proposition, and the streaming variant exists for the same reason, since matching a large tree can produce enough entries that buffering the whole array is wasteful:
import {globbyStream} from 'globby';
for await (const path of globbyStream('*.tmp')) {
console.log(path);
}Installation is one line, and the package requires Node 20 or newer:
npm install globbyWhy negation is not just a prefixed bang
The feature list leads with negated patterns like `['foo*', '!foobar']`, then goes further with negation-only patterns. A list containing nothing but negations, `['!foobar']`, matches everything except `foobar`, which is a different thing from returning an empty set and is worth understanding before you depend on it.
That expansion is automatic and controlled by `expandNegationOnlyPatterns`, which defaults to `true`:
import {globby} from 'globby';
// Default behavior: matches all files except .json
await globby(['!*.json']);
//=> ['file.txt', 'image.png', ...]
// Disable expansion: returns empty array
await globby(['!*.json'], {expandNegationOnlyPatterns: false});
//=> []The readme gives the reason to turn it off: when patterns come from user input, prepending a catch-all can match a great deal more than intended. This is the one option in the library with a security flavour, and it is worth setting explicitly if the pattern string is not entirely under your control.
Directory expansion is the other default that does work you would otherwise write by hand. A pattern of `foo` becomes `foo/**/*`, so pointing at a directory returns its contents instead of the directory itself. The `expandDirectories` option takes a boolean, an array of patterns to glob inside, or an object with `files` and `extensions`:
import {globby} from 'globby';
const paths = await globby('images', {
expandDirectories: {
files: ['cat', 'unicorn', '*.jpg'],
extensions: ['png']
}
});Reading gitignore files before traversal starts
The `gitignore` option defaults to `false`, and turning it on is not the same as filtering the results afterwards. According to the readme, globby searches for `.gitignore` files from the working directory downward, and if a `.git` directory turns up it also respects ignore files in parent directories up to the repository root, which matches how git itself applies patterns from parent directories to subdirectories.
The precedence runs the way git runs it: gitignore patterns take priority over your own. The way to include ignored files is to set the option to `false`. There is a subtlety about the separate `ignore` option, which filters results only. A pattern such as `'**/.gitignore'` is not treated as a candidate ignore file when globby goes looking, so it hides those files from the results without switching the feature off.
The performance note is the part that changes behaviour on a real repository. Globby reads the ignore files before globbing and hands fast-glob patterns for directories it can prove are ignored, so a large `node_modules` or build output directory is skipped during traversal rather than enumerated and filtered afterwards. The readme is explicit that this survives negation, including a case like `!important.log`: only rules that cannot be re-included are used to skip directories, while final filtering always matches git. If you want to read fewer files, `ignoreFiles: '.gitignore'` targets just the root file instead of searching recursively.
The generic form of the ignore option, and global git config
`ignoreFiles` is the broader version of the same idea. It takes glob patterns for ignore files using gitignore syntax, and the readme points at `.babelignore`, `.prettierignore` and `.eslintignore` as the kind of thing it exists for. It filters results only, just like `ignore`, and the same performance advice applies: a specific path beats a recursive pattern.
`globalGitignore` is the least obvious option and the most involved. It respects the global ignore file configured through `git config core.excludesfile`, including values from `[include]` and `gitdir` or `gitdir/i` `[includeIf]` sections, and treats those patterns as root-level patterns the way git does.
Its scope is deliberately narrow. It reads only user-level git config, from `GIT_CONFIG_GLOBAL`, `$XDG_CONFIG_HOME/git/config` and `~/.gitconfig`, and falls back to `$XDG_CONFIG_HOME/git/ignore` or `~/.config/git/ignore` when `core.excludesfile` is unset. Repository-local `.git/config` and system config are intentionally not consulted, and other `includeIf` predicates such as `onbranch:` are not supported. Those are stated limits rather than oversights, and they make the behaviour easier to reason about when a build runs in an unexpected environment.
There is a related constraint on custom filesystems. If you pass your own `fs` implementation and enable `gitignore`, `ignoreFiles` or `globalGitignore`, that adapter also needs `readFile` or `readFileSync`. With `globalGitignore` the requirements go further, needing `fs.promises.stat` or `fs.stat` for the promise and stream variants and `fs.statSync` for the synchronous one.
Forward slashes only, and the Windows trap
The readme puts a warning before the API reference rather than burying it, and it is the most likely thing to waste an afternoon. Glob patterns can only contain forward slashes. If you build a pattern from path components, you need `path.posix.join()` rather than `path.join()`.
The Windows consequence is stated without hedging: patterns with backslashes will silently fail. Silent is the operative word. Nothing throws, nothing warns, you get an empty array and start debugging your own logic.
There is a helper for the general case, `convertPathToPattern(path)`, which escapes characters that carry special meaning in globs, namely `()`, `[]` and `{}`, and on Windows also converts backslashes to forward slashes. That is the fix for literal paths containing those characters, not just for the separator problem:
import {globby, convertPathToPattern} from 'globby';The alternative for platform-neutral code is the `slash` package, which globby already depends on at version 5, along with `fast-glob`, `micromatch`, `ignore`, `is-path-inside`, `@sindresorhus/merge-streams` and `unicorn-magic`. Seven runtime dependencies is a lot on paper, though each maps to a specific capability above.
A project shaped by patch releases
The version line tells you something about where the attention goes. v16.2.4 fixed the `ignore` option disabling the `gitignore` option, v16.2.3 fixed backslash-escaped `.gitignore` rules, and v16.2.2 stopped enumerating ignored directories with the `gitignore` option. Three consecutive releases, all inside the gitignore path, all fixing behaviour rather than adding features.
That sequence is worth reading as a signal. Gitignore handling is where the complexity lives, and it is also where a wrong answer is quiet, because a file that should have been skipped is either present or absent and nothing complains.
The repository structure is correspondingly small. `index.js` and `index.d.ts` are the entry points named in the `exports` field, with `ignore.js` and `utilities.js` alongside them, and the `files` array in `package.json` publishes exactly those four files. Tests live in `tests/`, there is a `bench.js`, and `fixtures/` holds the sample trees. The test script is `xo && ava && tsd`, so linting, unit tests and type definition tests all run before the suite passes.
The last push to `main` was 2026-08-19, the same day v16.2.4 was published. For a library this widely depended on, that consistency matters more than the star count: a wrapper whose behaviour is quietly changing underneath you is a worse problem than one with fewer users.
Editorial conclusion
globby earns its place by handling the three things raw glob libraries leave to you: turning a directory into a recursive pattern, letting a negated pattern subtract from the set, and honouring the ignore files a repository already declares. The gitignore path is the part worth understanding rather than just switching on, since it reads files before traversal and hands the matcher patterns for directories it can prove are ignored. Two rough edges are worth knowing: patterns are forward-slash only and fail silently on Windows unless you convert them, and the project keeps no published release beyond a stream of patch versions. Start with `npm install globby` and a single `globby(['*', '!cake'])` call, then read the gitignore section before you rely on it in a build.
Frequently asked questions
What is the difference between globby and fast-glob?
globby is a wrapper, not a replacement. It sits on fast-glob and adds a promise API, negated and negation-only patterns, automatic directory expansion, support for gitignore-style ignore files, and URL support for `cwd`. Everything it does still ends up as fast-glob options, which is why the readme points you at the fast-glob option list for the rest.
How do I exclude a file from a glob pattern in globby?
Prefix the pattern with a bang inside the same array, as in `globby(['*', '!cake'])`, which returns everything except `cake`. A list of only negations works too, and globby prepends a catch-all so `['!foobar']` matches every file except `foobar`. Set `expandNegationOnlyPatterns` to `false` if you want that second form to return nothing instead, which is the safer setting when patterns come from user input.
Does globby respect .gitignore files?
Yes, once you set `gitignore` to `true`, since it defaults to `false`. It reads ignore files from the working directory downward and, if a `.git` directory is found, from parent directories up to the repository root. Gitignore patterns take priority over your own patterns, and ignored directories are skipped during traversal rather than filtered after.
Why does my glob pattern return nothing on Windows?
Patterns may only contain forward slashes. The readme says backslash patterns fail silently on Windows, which usually means building the pattern with `path.join()` instead of `path.posix.join()`. Run the value through `convertPathToPattern()` to escape glob metacharacters and normalise separators at the same time.
Official sources
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.
[](https://hysenlabs.com/projects/sindresorhus-globby)