Enumeratum: sealed-trait enumerations for Scala without reflection
A type-safe, reflection-free, powerful enumeration implementation for Scala with exhaustive pattern match warnings and helpful integrations.
At a glance
- What is it?
- Enumeratum is a Scala library for enumerations built on sealed traits and case objects, with a macro that discovers the members at compile time so pattern matches get exhaustive warnings and no reflection is needed at runtime. It suits a Scala team that wants the ergonomics of the standard Enumeration without the type erasure problem, and it ships separate modules for the common libraries.
- Who is it for?
- Adopt Enumeratum if you write Scala and want enumerations whose members are ordinary case objects with methods, with exhaustive pattern match warnings and no reflection at runtime. Skip it if you are on Scala 3 and cannot accept the two documented constraints, since all immediate parent types of enum entries must be sealed and ValueEnums need the `-Yretain-trees` Scalac option.
- 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 132 days ago.
- What is it written in?
- Mainly Scala, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The problem with the standard Enumeration
Enumeratum is a type-safe and powerful enumeration implementation for Scala that offers exhaustive pattern match warnings, integrations with popular Scala libraries, and idiomatic usage that will not break your IDE. The reason it exists is spelled out in the README's own framing. It aims to be similar enough to Scala's built-in `Enumeration` to be easy to use and understand while offering more flexibility, type-safety, and richer enum values without having to maintain your own collection of values. The type-safety point is not a style preference but the consequence of how the standard library works. The README links a blog post describing erasure on Scala's `Enumeration`, and the mechanism is that `Enumeration` and its `Value` members are built on a runtime representation, so the type of a value is not the type of your enum at the type level, and a pattern match over it is not checked for exhaustiveness. Enumeratum inverts that. The primary language is Scala, the licence is MIT, the last push was on 2026-06-13, and the releases are enumeratum-1.9.8 on 2026-06-13, 1.9.7 on 2026-03-28 and 1.9.6 on 2026-03-07.
The declared advantages, and which ones matter
The niceties list is long, and a few entries carry more weight than the rest. Zero dependencies is the first. It is performant, described as faster than `Enumeration` in the standard library, with a benchmarking section. Your enum members can be full-fledged normal objects with methods, values and inheritance, which is the thing you actually want from an enumeration and the thing the standard library makes awkward. There is no usage of reflection at runtime, which the README says may also help with performance but means Enumeratum is compatible with ScalaJS and other environments where reflection is a best effort, such as Android. There is no usage of `synchronized`, which may help with performance and deadlock prevention. All magic happens at compile time, so you know right away when things go awry. And the idiom claim is that you are very clearly still writing Scala, with no funny colours in your IDE, which is a remark about a library that would otherwise need its own DSL.
How a value enum is declared
The usage model is that you declare your own sealed trait or class that extends `EnumEntry` and implement it as case objects inside an object that extends from `Enum[A]`:
import enumeratum._
sealed trait Greeting extends EnumEntry
object Greeting extends Enum[Greeting] {
val values = findValues
case object Hello extends Greeting
case object GoodBye extends Greeting
case object Hi extends Greeting
case object Bye extends Greeting
}The one line that does the work is `val values = findValues`, and the README explains it. `findValues` is a protected method that invokes a macro to find all `Greeting` object declarations inside an `Enum`, and you use it to implement the `values` member. That is the whole trick, and it is why there is no runtime reflection and no hand-maintained list of members that can drift out of date. The members are discovered from the source at compile time, so adding a case object to the object automatically adds it to `values`, and the exhaustiveness check on a pattern match over the sealed trait is the compiler's own check on the sealed hierarchy.
Looking up an entry by name, in six ways
The object gets a set of lookup methods, and the variants cover the cases where a plain `withName` would throw. `Greeting.withName("Hello")` returns the entry, and `Greeting.withName("Haro")` throws a `NoSuchElementException` with the message that Haro is not a member of Enum, listing the members. The safer alternative is `withNameOption`, which returns an `Option[Greeting]`, so a miss is `None` rather than an exception. Then there are the case-insensitive and case-restricted variants, each with an Option-returning counterpart. `withNameInsensitive` matches a string regardless of case, `withNameUppercaseOnly` requires an uppercase string, and `withNameLowercaseOnly` requires a lowercase one. The documentation shows the uppercase-only Option variant returning `None` for a mixed-case string, so the restricted variants are strict rather than lenient. The reason to have all of them is that the string arriving at your boundary came from somewhere, a URL segment, a database column, a config file, and how strict you want to be about it is a decision that differs per call site.
ValueEnum, and the compile-time uniqueness constraint
The `ValueEnum` trait is the part that maps an enum to a primitive value, and the README describes the members as having compile-time uniqueness constraints. The distinction from the plain `Enum` is worth holding onto, because it is the difference between an enumeration and a lookup table. A plain enum has a name and an ordinal. A value enum has an explicit mapping to a primitive, so a value stored in a database or sent over the wire is defined by the enum rather than by an index that changes when someone reorders the file. The uniqueness constraint is the safety mechanism, since two members mapping to the same primitive value is a bug the compiler catches rather than a runtime surprise. The manual pages and the manual override section cover the details, and the documentation also describes a manual override of name and mixins to override the name, which is how you control the string that `withName` and the serialisers see.
The integration modules, and the Scala 3 constraints
Enumeratum publishes for Scala 2.12.x, 2.13.x and 3.x as well as ScalaJS and ScalaNative, and the integrations are separate modules that you add to your build à la carte rather than pulling in a bundle. The list is long, with Play on the JVM, Play JSON on the JVM and ScalaJS, Circe on the JVM and ScalaJS, ReactiveMongo BSON on the JVM, Argonaut on the JVM and ScalaJS, Json4s on the JVM, ScalaCheck on the JVM and ScalaJS, Slick on the JVM, Quill on the JVM and ScalaJS, sttp tapir on the JVM and ScalaJS, and Scalafix. Cats and Doobie also appear in the table of contents. Scala 3 comes with two constraints you need to know before you adopt it. All immediate parent types of enum entries need to be sealed, and the `-Yretain-trees` Scalac option must be set when using ValueEnums. The repository root reflects the module split, with directories for each integration alongside `enumeratum-core/`, `macros/`, `aggregates/`, `benchmarking/` and a test module.
Where Enumeratum does not fit
Two limits are worth naming. The first is that the library is not a general-purpose enum type, it is specifically a sealed-trait based implementation, so you cannot use it to enumerate over types that are not statically known, and you inherit the constraint that every member must be a case object in a known object. That is the price of the exhaustive match warning, and for most domain models it is a price worth paying, but it is a real constraint rather than a preference. The second is the integration surface. Because each library has its own module and the JVM and ScalaJS variants differ, an application that uses two of these libraries and needs to run on both platforms ends up managing a matrix of module dependencies, and a library that has not published a module for your combination is a gap you have to work around rather than a configuration you can adjust. The repository also carries an `AGENTS.md` and a `CLAUDE.md` at the root and a `.scalafmt.conf`, which reflects a project with a settled contribution workflow, and the CI workflow badge is next to a codecov badge, so coverage is measured on every change.
Editorial conclusion
Adopt Enumeratum if you write Scala and want enumerations whose members are ordinary case objects with methods, with exhaustive pattern match warnings and no reflection at runtime. Skip it if you are on Scala 3 and cannot accept the two documented constraints, since all immediate parent types of enum entries must be sealed and ValueEnums need the `-Yretain-trees` Scalac option. Before you upgrade, check that the integrations you use have a published module for your Scala version, since the library is split à la carte, and remember it is published for Scala 2.12.x, 2.13.x and 3.x as well as ScalaJS and ScalaNative.
Frequently asked questions
How do I declare an enum with Enumeratum?
Declare a sealed trait extending EnumEntry and implement it as case objects inside an object extending Enum[A], with `val values = findValues`. The findValues method invokes a macro to find all the object declarations inside the Enum, so the member list is discovered at compile time.
How do I add Enumeratum to an SBT build?
Set the version in a variable and add the dependency: `libraryDependencies ++= Seq("com.beachape" %% "enumeratum" % enumeratumVersion)`. The integrations are separate modules that you add à la carte.
Does Enumeratum use reflection at runtime?
No. All the magic happens at compile time, and the absence of runtime reflection is what makes it compatible with ScalaJS and other environments where reflection is a best effort, such as Android. It also avoids using synchronized.
What are the Scala 3 limitations of Enumeratum?
All immediate parent types of enum entries need to be sealed, and the -Yretain-trees Scalac option must be set when using ValueEnums.
How do I look up an enum member by name safely?
Use withNameOption, which returns an Option instead of throwing. There are also withNameInsensitive, withNameUppercaseOnly and withNameLowercaseOnly, each with an Option-returning counterpart for case handling.
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/lloydmeta-enumeratum)