ow tells you which library to use instead, and its automatic labels stop working in the browser
Function argument validation for humans
At a glance
- What is it?
- A small runtime validator for function arguments, command line flags and environment variables, from a single-line assertion down to a chainable predicate API with TypeScript narrowing. It states its own limits, it names a different library for complex schemas, and its development-only entry point only shrinks your bundle if your bundler substitutes an environment variable at build time.
- Who is it for?
- ow is a good fit for the narrow job it names: checking a handful of arguments at the boundary of a function, a command line parser or a configuration read, where a thrown error with a parameter name is more useful than a stack trace three frames later. Three things to know.
- 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 57 days ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on November 14, 2026, and from our analysis. They are not legal advice.
Star history
- sindresorhus/ow3,865 ★ · +2
| Repository | 2026-09-24 | 2026-11-14 |
|---|---|---|
| sindresorhus/ow | 3.9k | 3.9k |
2 Stars in the last 51 days, measured from the daily GitHub snapshot. Compare with other projects
Editorial analysis
The library says when not to use it
The first section is a short argument for runtime validation, and then it names a competitor. TypeScript only checks types at compile time, so a compiled program can still receive unexpected data from function arguments holding untrusted input, from command line arguments and flags, or from environment variables and configuration files. Those three are the scope. Then the sentence: for complex schema validation, covering API responses, database queries and JSON parsing, use a different library instead. A small argument checker pointing you at a schema library when you outgrow it is a rarer thing to read than it should be, and it tells you exactly where the boundary is. If your data arrives as a document, this is the wrong tool by its own account, and you have been told in one sentence rather than by discovering it. The whole surface is one line to install and one line to use:
npm install owow(input, ow.string.minLength(5));Everything after that is the same call with a longer predicate chain.
Automatic labels are a Node feature, not a general one
The highlights promise the same validation on a server, in a browser or in command line tools. The API section then says the label is inferred automatically in Node.js, that you can override it, and that the automatic inference does not work in the browser. So the error quality is platform dependent: on a server a failed assertion names the offending parameter, and in a browser it does not unless you pass the label yourself. The manifest shows the same split in a second place. Two of the six runtime dependencies exist for label inference, one that inspects call sites and one that identifies names, and a browser field in the manifest redirects the label inference module to a browser-specific build. The cost of the convenience is visible if you look at the dependency list rather than at the example.
The development-only import only works if your bundler cooperates
There is one note, and every clause of it is conditional. If you intend to use the library for development purposes only, import from the development-only entry point instead of the usual one, and run the bundler with the environment variable set to production at build time. The package will then export a shim in production, which should give a significantly lower bundle size. The substitution has to happen inside the bundler while it builds. Setting the variable when the process runs does nothing for this, and a bundler that does not perform the substitution ships the whole validator with the shim unused. The note gives you the flag and the example command but not the check, so verify the bundle size rather than trusting the import path.
Two entry points, one type declaration, a two-entry tarball
The exports map has two entries and they share a single type declaration file, which is why the development-only import still narrows types in the editor while costing nothing at run time. The published file list contains exactly two things: the build output and the development-only shim. No sources, no tests and no example ship to a registry consumer. Those live at the repository root instead, in lowercase files alongside a lowercase licence and a lowercase readme, with a package script that builds and then runs the example. The example is therefore something you read in the repository rather than something you can import from the installed package, which is a deliberate trade for a small library and one worth knowing if you were hoping to copy a working file.
A year of commits past the newest tag
The releases are v3.0.0 on 2025-09-09, v3.1.0 on 2025-10-10 and v3.1.1 on 2025-10-16. A major version, then two patches inside five weeks, and then nothing published since. The last push to the default branch is dated 2026-09-18, so roughly eleven months of work sit on the branch with no tag and no prerelease channel to reach them from. For a library this small, that gap is unusual enough to check before you depend on it, because the version you install is a snapshot from the end of 2025 rather than the current state of the code. If a change you need is on the branch, your options are a commit reference or a fork, and neither is documented anywhere in the repository.
The release path runs the linter, the types and the tests before publish
The scripts describe a release process with more care than most packages this size have. The test command runs the linter first, then loads TypeScript through an ESM loader, then a coverage tool, then the test runner. The build script deletes the output directory before compiling, so a stale artefact cannot survive a build. A publish-only hook runs the tests, which means a release cannot skip them even if someone forgets. After publishing, a post-publish hook generates the API documentation from the single source entry point with a typedoc call and pushes it to GitHub Pages without history. Type-level behaviour is tested too: the development dependencies include a type-testing package, which is what you would use to prove that a value of unknown type becomes a string after a validation.
Six runtime dependencies, two of them for the label
The dependency list is worth reading against the example, because the example is one line. Six runtime packages: the author's own type guard helper, one that inspects call sites, one that identifies names, one for dotted property access, one for fast deep equality, and one for reading environment variables. Two of those exist so an assertion can name its own parameter, two exist for deep object predicates, and the type guard helper is the thing that does the narrowing. That is a reasonable shape for what the library does, and it is more than the single call in the usage example suggests. The Node floor is 20, the package is declared as a module with no side effects, and the keyword list covers type guards, assertions and predicates.
Editorial conclusion
ow is a good fit for the narrow job it names: checking a handful of arguments at the boundary of a function, a command line parser or a configuration read, where a thrown error with a parameter name is more useful than a stack trace three frames later. Three things to know. It says in its own first section that complex schemas belong to a different library, so do not reach for it when you are validating a JSON document. Automatic label inference is Node only, so the same validation produces less informative errors in a browser. And the size-saving import is conditional on your bundler: it works when the variable is substituted at build time and silently does nothing when it is not. Check the bundle rather than assuming the shim is active. Also note that the newest published release is v3.1.1 from 2025-10-16 while the last push is dated 2026-09-18, so a year of work on the default branch is not in what npm installs. Licence terms are MIT and the Node floor is 20.
Frequently asked questions
What is ow used for?
Runtime validation of function arguments, command line arguments and flags, and environment variables and configuration files. The README also says that for complex schema validation of API responses, database queries or JSON parsing you should use zod instead.
Does ow work in a browser?
Yes, the highlights list the same validation on a server, in a browser and in command line tools. Automatic label inference, however, is documented as Node.js only and explicitly does not work in the browser, so error messages there carry less detail unless you pass a label yourself.
How do I keep ow out of my production bundle?
Import from the development-only entry point instead of the main one, and run your bundler with the environment variable set to production at build time, which makes the package export a shim and should give a significantly lower bundle size. The substitution has to happen in the bundler, not at run time.
What does ow require from Node?
Node 20 or later, as declared in the package manifest. The package is declared as a module with no side effects, and its exports map offers a main entry and a development-only entry that share one type declaration file.
When was ow last released?
The three newest releases are v3.0.0 on 2025-09-09, v3.1.0 on 2025-10-10 and v3.1.1 on 2025-10-16. The last push to the default branch is dated 2026-09-18, so the branch has moved well past the newest tag.
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-ow)Star history chart for your README
[](https://hysenlabs.com/projects/sindresorhus-ow)Was this useful?