15 Elixir Libraries I Reach For in 2026

The Hex packages I install on day one of a new Elixir project in 2026 — what each earns its place doing, and the ones I dropped along the way.

TL;DR: A curated day-one stack beats the awesome-list dump every time. The famous awesome-elixir page is a thousand links sorted alphabetically — useful as a reference, useless as a decision. This is the opposite: the ~15 packages I actually mix deps.get on the first morning of a new Elixir project in 2026, the one job each earns its place doing, and the libraries I dropped to get here (HTTPoison, Tesla, Quantum, hand-rolled mocks). The honest tradeoff: a tight stack means you sometimes write 20 lines you could have pulled from a dependency — and that’s the trade I take every time, because a dependency you don’t understand is a liability you can’t debug at 2am.

There’s a moment on every new project where the deps block in mix.exs is empty and you have to decide who you’re inviting to the party. Get it right and the next six months are smooth. Get it wrong — pick the unmaintained thing, the over-abstracted thing, the thing with no escape hatch — and you spend those months fighting your own foundation.

I’ve made enough of those mistakes to have opinions. What follows isn’t every good library; it’s the ones I reach for by default, before I know much about the project, because they’ve earned trust across many of them. At the end I’ll give you the rubric I use so you can judge your own stack instead of cargo-culting mine.

A note on what this is not: it’s not a substitute for mix phx.new. Phoenix, Plug, Telemetry, and friends come in the box. This is about the deliberate additions on top.

HTTP & talking to the outside world

Req — the single most consequential default I’ve changed in years. Req is a batteries-included HTTP client built on top of Finch, and it is the answer to “what do I use to make an HTTP request in Elixir?” in 2026. Retries, redirects, JSON encode/decode, compression, and streaming all work out of the box with sane defaults. I reach for it the instant a project needs to call anything — a payment API, an LLM endpoint, a webhook.

This is the clearest “what I dropped” story in the whole list. I used to reach for HTTPoison, then Tesla. HTTPoison is effectively in maintenance mode and its ergonomics show their age. Tesla is still actively maintained and genuinely good — its middleware model is powerful — but for most projects Req gives me 95% of what Tesla’s middleware stack did with a fraction of the ceremony. I keep Tesla in my back pocket for the rare case where I need a very specific adapter or a custom middleware pipeline shared across many clients. For everything else, Req. (José Valim maintains it; that’s not why it’s good, but it’s a decent proxy for “this will still be here in three years.”)

Finch — I rarely write Finch.request/2 directly anymore, because Req sits on top of it. But Finch is what I name explicitly when I need connection pooling tuned for a specific high-volume downstream — a metrics sink, an LLM provider I’m hammering. Knowing it’s the layer underneath Req is the difference between “the HTTP client is slow” and “I need a dedicated pool with 50 connections to this one host.” Reach for it directly when pool topology matters.

Jason — JSON in and out. It’s so ubiquitous that half your other deps already pull it in. One honest caveat worth knowing in 2026: Jason’s last stable release predates much of this list — recent versions on Hex are alpha-tagged. In practice it’s rock-solid and battle-tested at enormous scale, so I still install it without hesitation, but it’s worth keeping an eye on whether the wider ecosystem shifts toward stdlib JSON (Elixir now ships a built-in JSON module) over the next year. For now, Jason stays.

Data & Ecto

Ecto + ecto_sql — not optional, not interesting to argue about. If your project touches a database, Ecto is the data-mapping and query layer, full stop. What’s worth saying to newer Elixir developers: resist the urge to treat changesets as annoying boilerplate. They’re the single best place to validate input at the boundary, and leaning into them is how you keep garbage out of your domain. I write changesets even for embedded schemas that never hit a database, purely for the validation pipeline.

The Ecto-adjacent thing I’ll flag: separate queries for has_many, JOINs for belongs_to. The number of N+1 problems and accidental Cartesian products I’ve reviewed because someone reached for a clever single query is not small. Ecto gives you the tools to do it right; the discipline is on you.

Background work — the part Elixir does better than anyone

Oban — if I could only install one library from this entire list, it would be Oban. Postgres-backed background job processing with a real database transaction guarantee, a UI, cron, uniqueness, rate limiting, and a maturity level that’s frankly rare in any ecosystem. The day-one move on any project with a database: add Oban before you need it, because the first time you reach for “I’ll just spawn a Task” for something that absolutely must not be lost, you’ll wish you had it.

Two non-negotiables I enforce on every Oban job, no exceptions: jobs must be idempotent (they will run twice eventually), and args must use string keys and contain only serializable data — never a struct, never a PID. I’ve written a deeper piece on driving AI agents through Oban queues (coming soon — the durable-execution-for-LLM-calls pattern deserves its own post), because the retry/backoff/uniqueness machinery turns out to be exactly what flaky, expensive model calls need.

Broadway — not every project, but I reach for it the moment the shape is “ingest a firehose and process each message with backpressure” — SQS, Kafka, RabbitMQ, GCP Pub/Sub. Oban is for jobs you enqueue; Broadway is for streams you consume. Knowing which problem you have keeps you from bending one into the other. Skip it entirely if you don’t have a real pipeline — it’s not a default, it’s a when-you-need-it.

A note on scheduling: I no longer install Quantum for new projects. It’s a fine cron-style scheduler, but Oban Cron covers the same ground with the same durability guarantees as the rest of my background work, so I’d rather have one mental model than two. If a project already has Quantum and no Oban, that’s fine — I just don’t add it fresh.

Web & LiveView

Phoenix LiveView — the reason a one-person team can ship interactive product without a separate frontend stack. I default to LiveView for essentially all internal tools and most product UI, and only reach for a heavier client-side approach when there’s a genuine offline or ultra-low-latency requirement. The two disciplines that separate good LiveView from janky LiveView: use streams for any list that can grow past a hundred items (don’t hold ten thousand rows in socket assigns), and never run an unconditional DB query in mount — use assign_async or gate on connected?/1. I go deeper on the runtime model that makes this cheap in Elixir’s concurrency model and on why the BEAM is a natural fit for agentic workloads.

Bandit — the pure-Elixir HTTP server that’s now the default under new Phoenix projects, replacing Cowboy. I don’t usually add it so much as keep it — but I’ll call it out because if you’re upgrading an older app still on Cowboy, moving to Bandit is one of the lower-risk, higher-clarity upgrades available: a stack trace that stays in Elixir instead of disappearing into Erlang internals is worth a lot when something goes wrong.

Testing & developer experience

Mox — mocks based on explicit behaviours, not magic. This is the one that enforces an architecture I’d want anyway: define a behaviour for your boundary (the payment gateway, the LLM client), mock that, and your production code is forced to depend on a contract rather than a concrete module. I reach for it on day one specifically because retrofitting “wrap third-party APIs behind a behaviour” later is painful. Mox is mature and stable — infrequent releases here are a feature, not neglect. The thing I dropped: hand-rolled mock modules and :meck-style runtime patching, both of which let you mock things that should have been refactored instead.

ExMachina — test factories. The job it earns its place doing: making test data setup readable so your tests document intent instead of drowning in struct literals. Mature, low-cadence, exactly what you want from a factory library. I’ll note the honest alternative — many teams now prefer plain factory functions in a test support module over a library — and that’s a legitimate choice. I still reach for ExMachina out of habit and because build/insert/params_for cover the cases I hit; if you’re starting fresh and want zero deps, plain functions are fine.

StreamData — property-based testing. I don’t property-test everything, but for anything with a parser, a serializer, a state machine, or non-trivial data transformation, generating a thousand random valid inputs finds the edge case your three hand-written examples missed. Reach for it on the gnarly 20% of the codebase; example-based tests for the rest.

Credo — static analysis for consistency. On a solo project it’s a second opinion; on a team it’s how you stop bikeshedding style in code review. I wire it into CI on day one so the conversation is “the linter says” rather than “I personally prefer.”

Dialyxir — the friendly Mix wrapper over Dialyzer for success typing. The honest take: Dialyzer’s error messages can be genuinely cryptic, and the first PLT build is slow. But the class of bug it catches — passing the wrong shape into a function three call-hops away — is exactly the class that’s expensive to catch any other way. I add it, I tune it to not be noisy, and I accept that it’s a long-term investment, not an instant payoff. Skip it on a throwaway prototype; add it the moment the project is going to outlive the quarter.

Sobelow — security-focused static analysis built specifically for Phoenix. It catches the Phoenix-shaped footguns: missing CSRF protection, raw/1 on untrusted content (XSS), SQL injection via string interpolation, config leaks. Runs in seconds, lives in CI, and the false-positive rate is low enough that the signal is worth the noise. Day-one install on anything with a web surface.

The AI-era stack — where Elixir is quietly excellent

This is the section that didn’t exist on my list three years ago, and it’s the reason this post anchors the site’s Elixir-and-AI cluster. The short version: the BEAM is a genuinely good place to run AI workloads — concurrent, fault-tolerant, with first-class streaming — and I’ve argued separately that Elixir is one of the languages AI writes best. Here’s what I install.

Nx — multi-dimensional tensors and numerical computing, the foundation of the entire Elixir ML stack. You rarely use it directly in a product, but it’s the layer everything else stands on, and when you need to do math on embeddings — cosine similarity, normalization — this is where it lives.

Bumblebee — pre-trained transformer models (embeddings, Whisper, BERT-family, and more) running in your BEAM process, no Python sidecar. The single highest-leverage use I reach for: generating embeddings locally for search and RAG instead of paying a per-token API for every chunk. It’s a young library (downloads are modest), so I treat it as “excellent for the embedding/inference cases it covers” rather than “drop-in for any model on HuggingFace.” Verify your specific model is supported before betting on it.

Instructor — structured, schema-validated output from LLMs. You define an Ecto schema, the library coaxes the model into returning data that conforms to it, and validation failures get fed back to the model to retry. The honest status: it’s early-stage with low adoption and an infrequent release cadence, so I wrap it behind my own boundary module (so I can swap the implementation) rather than letting it leak through the codebase. But the pattern — “make the LLM return validated structs, not strings I have to parse” — is non-negotiable for any serious LLM feature, and Instructor is the cleanest expression of it in Elixir today. If you’d rather not take the dependency, the pattern is reproducible in ~50 lines with Req + Ecto changesets + a retry loop.

pgvector — vector columns and nearest-neighbor search wired into Ecto. This is my honest recommendation for RAG in Elixir, and it’s deliberately not a RAG framework. There are dedicated RAG libraries on Hex — arcana and rag_ex both exist and have had recent releases — but their adoption is so thin (hundreds to low-thousands of downloads) that I won’t put them under a production feature yet. What I actually build with is the boring, durable combination: pgvector for storage and similarity search, Bumblebee or an embedding API for the vectors, and Req for the LLM calls — no framework in the middle. Postgres you already operate, queries you can read, an escape hatch at every layer. I’m writing a full walkthrough of this exact stack (coming soon); the headline is that you do not need a RAG framework to ship RAG in Elixir, and in 2026 you’re better off without one.

Operations — the two I add before I think I need them

Hammer — rate limiting with pluggable backends. The day-one case: you have one external API endpoint, one expensive LLM call, one auth route that you do not want hammered, and Hammer gives you a clean token-bucket without standing up Redis if you don’t want to. Cheap to add, painful to retrofit under load.

PromEx — Prometheus metrics plus pre-built Grafana dashboards for the libraries you’re already running (Phoenix, Ecto, Oban, the BEAM itself). I add observability before the incident, not during it. The “before I need it” framing is the whole point: the metrics you wish you had are always the ones you didn’t instrument.

tzdata — the unglamorous one. If your app does anything with time zones, you need a tz database, and tzdata is it. I mention it only because forgetting it produces confusing runtime errors the first time you try to shift a datetime into a named zone. Install it, configure it to auto-update, move on.

The rubric — how to judge your own stack

I didn’t pick these by popularity. Each one clears four bars, and you can apply the same bars to anything you’re tempted to add:

Criterion The question I ask
Maintained Is there a recent release, or is recency simply unnecessary because it’s done? (Mox being quiet is fine; a half-finished thing being quiet is not.)
Does one thing Can I describe its job in a single sentence? Libraries that do five things are five things that can break.
Escape hatch When it doesn’t do what I need, can I drop to the layer below — or am I trapped inside an abstraction? (Req → Finch is the model here.)
Debuggable at 2am When it fails in production, does the stack trace make sense, and could I read the source if I had to?

A dependency that fails any of these is one I’d rather replace with code I own. That’s the trade behind the whole list: I’ll write a little more myself to depend on a little less that I don’t understand.

The verdict

Layer Reach for Skip / dropped
HTTP Req (Finch underneath) HTTPoison; Tesla unless you need its middleware
JSON Jason (watch stdlib JSON)
Data Ecto + ecto_sql
Background jobs Oban Quantum (Oban Cron covers it)
Stream ingestion Broadway (when you have a pipeline)
Web UI Phoenix LiveView + Bandit Heavy SPA unless truly needed
Testing Mox, ExMachina, StreamData hand-rolled mocks; :meck
Static analysis Credo, Dialyxir, Sobelow
AI / ML Nx, Bumblebee, Instructor, pgvector dedicated RAG frameworks (too early)
Ops Hammer, PromEx, tzdata

Fifteen-ish names, but really one principle: a small stack of well-understood, single-purpose libraries with escape hatches beats a sprawling one you can’t fully reason about. Steal the ones that fit your project, run them through the rubric, and drop anything that fails it — including, if it fails for you, anything on this list.

If you’re building on the BEAM for AI specifically, start with why the runtime fits agentic work, then the concurrency model that makes it cheap. The Oban-for-agents and pgvector-RAG deep dives are next in the cluster — and they’re built on exactly this stack.