NimbleOptions: validating and documenting Elixir options from one definition
A tiny library for validating and documenting high-level options
At a glance
- What is it?
- NimbleOptions is a small Elixir library that takes a keyword list describing your options and both validates user input against it and generates documentation from it, so the schema for a library's configuration is written once instead of being hand-maintained in a validation function and a docs page.
- Who is it for?
- Adopt NimbleOptions when you publish an Elixir library with a configuration surface, because writing the definition once and deriving both validation and docs from it removes a class of drift. Do not adopt it for validating a fixed set of internal values, where a plain pattern match is clearer.
- Can I use it commercially?
- Yes. Apache-2.0 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 59 days ago.
- What is it written in?
- Mainly Elixir, 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
One definition, two outputs
NimbleOptions is a tiny library for validating and documenting high-level options. The library allows you to validate options based on a definition, and a definition is a keyword list specifying how the options you want to validate should look like.
That is the whole idea, and it is worth stating what it replaces. A library that takes configuration usually ends up with a hand-written validation function on one side and a hand-written options table in the documentation on the other. The two drift apart the moment someone adds a key to one and forgets the other. Here, the definition is the single source, and the library produces both the validator and the documentation from it.
The second capability is that NimbleOptions is also capable of automatically generating documentation for a definition by calling NimbleOptions.docs/1 with your definition. Documentation generation is what turns this from a validation helper into a design tool: the shape of your options is expressed in a form that both a runtime check and a doc renderer understand.
The primary language is Elixir, the licence is Apache-2.0 with copyright 2020 Dashbit, and the default branch is main. The repository is not archived and the last push was on 2026-08-25, and there are no GitHub releases, which is normal for a Hex package, since versioning happens through the mix.exs and the Hex registry rather than through release tags on the repository.
What a definition looks like
A definition is a keyword list, and each option is a keyword list of its own describing the type, the default and whether it is required.
definition = [
connections: [
type: :non_neg_integer,
default: 5
],
url: [
type: :string,
required: true
]
]Three keys appear in this example, and they cover most of what a library needs. The type is an atom such as :non_neg_integer or :string. The default is the value substituted when the option is absent. The required flag marks an option the caller must supply.
The shape is Elixir's own keyword-list convention rather than a bespoke struct, which matters more than it first appears. A definition is data, so it can be built at runtime, passed to a function, and stored in a module attribute without any macro or compile-time construct. There is no definition DSL to learn, and no compile step that has to be re-run when the options change.
The type vocabulary is Elixir's own, so :non_neg_integer and :string mean what they mean everywhere else in an Elixir codebase, and a reader who knows Elixir already knows how to read a definition.
Validating, and what an error looks like
Validation goes through NimbleOptions.validate/2, which takes the options and the definition.
options = [url: "https://example.com"]
NimbleOptions.validate(options, definition)
#=> {:ok, [url: "https://example.com", connections: 5]}Two things are visible in the success case. The result is an ok tuple rather than a bare value, which is the idiomatic Elixir shape for a fallible call and composes with with and case. And the returned options include connections: 5, the default, even though the caller only supplied url. Applying defaults is part of validation, not a separate step you remember to do.
When the options do not match the definition, an error is returned.
NimbleOptions.validate([connections: 3], definition)
{:error,
%NimbleOptions.ValidationError{
keys_path: [],
message: "required option :url not found, received options: [:connections]"
}}The error struct carries two things beyond the message. keys_path is the path to the offending key, which is how a nested definition reports where the failure happened rather than just that something failed. And the message names both the missing option and what was actually received, so the caller can see the mismatch rather than a bare rejection.
The value of that struct over a plain string is that it is a struct. A library that wants to behave differently for a missing required option than for a bad type can pattern match on the struct, and a caller who wants to log the details can format the struct rather than re-parsing a sentence.
Adding it to a Mix project
The installation is a dependency entry in mix.exs.
def deps do
[
{:nimble_options, "~> 1.0"}
]
endThat is the entire procedure. There is no generator, no configuration file and no compile-time hook to set up, and the version requirement of about 1.0 pins the major series so a future 2.0 with a different definition format would not be pulled in silently.
The package is published on Hex as nimble_options, and the online documentation is at hexdocs.pm/nimble_options. The repository is a normal Elixir library layout: lib/ for the source, test/ for the test suite, mix.exs and mix.lock for the project, a .formatter.exs, a CHANGELOG.md and a LICENSE.md.
It is also worth noting the family the project belongs to. The README lists the other nimble libraries by Dashbit: NimbleCSV for simple and fast CSV parsing, NimbleOptions itself, NimbleOwnership for resource ownership tracking, NimbleParsec for simple and fast parser combinators, NimblePool for a tiny resource-pool implementation, NimblePublisher for a minimal filesystem-based publishing engine with Markdown support and code highlighting, and NimbleTOTP for tiny library for generating time-based one time passwords.
That list is a design signal. Each of these is deliberately tiny and does one thing, and several of them, NimbleOptions, NimbleParsec and NimblePool, are the kind of building block a library author reaches for instead of pulling in a framework. If you adopt one of these, you are likely to end up taking a second.
Where this stops being the right tool
There are three limits, and the first is about what kind of value you are validating.
The first is that a definition describes a fixed set of options with fixed types. That is a schema, and a schema is exactly what you do not want when the value space is open, for example a list of arbitrary terms, a map of user-defined metadata, or a callback. A pattern match handles those cases in fewer lines than a definition would, and it fails with a clearer message. The library is for a known set of named options with known types, which is what a library's public configuration is.
The second is that the documentation output is derived from the definition, so the definition has to carry everything a reader needs. If your option needs a paragraph of explanation or a conditional note, that context lives in the docs and in the typespec, not in the definition, and the generated documentation is only as good as what the definition can express. A definition with a type of :string cannot tell the reader what format that string should be in.
The third is the age of the repository and the absence of a release trail. The last push was on 2026-08-25, which is recent, but there are no GitHub releases and the version constraint of about 1.0 is the only signal about the current major. Depending on a library for a public API surface is a decision that needs a look at the CHANGELOG and the hexdocs, because a library that is actively touched but not versioned through visible tags is a library where you should read the diff before upgrading.
For comparison, the alternatives are a hand-written validation function, which is what most libraries do and which duplicates the documentation, and a schema library with more features, which brings a dependency and a larger API in exchange for capabilities the small definition format already covers. For a library with three options, NimbleOptions is the right size. For a library with thirty options across nested structures, the question is whether the generated documentation is worth the definition format, and that depends on how much of the schema is nested.
Editorial conclusion
Adopt NimbleOptions when you publish an Elixir library with a configuration surface, because writing the definition once and deriving both validation and docs from it removes a class of drift. Do not adopt it for validating a fixed set of internal values, where a plain pattern match is clearer. Before you depend on it, read the CHANGELOG and the docs for the definition format, since the repository has no GitHub releases and the last push was on 2026-08-25.
Frequently asked questions
What does NimbleOptions do in Elixir?
It validates options against a definition, which is a keyword list describing the type, default and required flag of each option, and it can also generate documentation for that same definition with NimbleOptions.docs/1, so a library's configuration schema is written once.
How do I add nimble_options to a Mix project?
Add it to the dependencies list in your mix.exs with {:nimble_options, "~> 1.0"} and fetch it as usual. There is no generator or configuration file, and the version requirement pins the 1.0 series.
What does NimbleOptions.validate/2 return?
It returns an ok tuple containing the validated options with defaults applied, so an option with a default appears in the result even if the caller did not supply it. On a mismatch it returns an error tuple holding a NimbleOptions.ValidationError struct with a keys_path and a message.
Does a failed validation report which option was wrong?
Yes. The error message names the missing required option and what was received, for example required option :url not found, received options: [:connections], and the struct's keys_path carries the path to the offending key so nested definitions report where the failure happened.
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/dashbitco-nimble-options)Was this useful?