# PureConfig: configuration loading derived from the type at compile time

> PureConfig is a Scala library that reads HOCON, JSON and Java .properties files into native Scala classes without hand written deserialisers, using a derivation mechanism to generate the loading code at compile time. It builds on Typesafe Config for the file handling, and the parts worth understanding are the derivation, the precedence rules and the fact that the result is an Either.

**pureconfig/pureconfig** — A boilerplate-free library for loading configuration files

- Repository: https://github.com/pureconfig/pureconfig
- Website: https://pureconfig.github.io
- Stars: 1,540 · Forks: 183
- Language: Scala
- License: MPL-2.0
- Published: 2026-10-16 · Updated: 2026-10-16 · Language: en
- Canonical page: https://hysenlabs.com/projects/pureconfig-pureconfig

## You say what to load, the compiler says how

PureConfig is a Scala library for loading configuration files, and the reason section in the README is the clearest statement of what it does.

Loading configurations has always been a tedious and error-prone procedure. A common way to do it consists in writing code to deserialize each field of the configuration. The more fields there are, the more code must be written, and tested, and maintained, and this must be replicated for each project.

The argument is that this kind of code is boilerplate, because most of the time the code can be automatically generated by the compiler based on what must be loaded. If you are going to load an Int for a field named foo, then probably you want some code that gets the values associated with the key foo in the configuration and assigns it to the proper field after converting it to Int.

The goal, stated in the README, is to create at compile-time the boilerplate necessary to load a configuration of a certain type. In other words, you define what to load and PureConfig provides how to load it.

That is the whole design, and it is a good one for the case it targets. A configuration in a Scala service is usually a case class, and the compiler already knows the shape of that case class. Deriving the loader from the type means the loader cannot drift from the type, and a renamed field becomes a compile error rather than a runtime surprise.

## Sealed traits are the case that makes derivation worth the trouble

The quick start example is small, and it is chosen to show the two features that matter most.

```scala
import pureconfig._
import pureconfig.generic.auto._

case class Port(number: Int) extends AnyVal

sealed trait AuthMethod
case class Login(username: String, password: String) extends AuthMethod
case class Token(token: String) extends AuthMethod
case class PrivateKey(pkFile: java.io.File) extends AuthMethod

case class ServiceConf(
  host: String,
  port: Port,
  useHttps: Boolean,
  authMethods: List[AuthMethod]
)
```

The ordinary fields are uninteresting, because deriving a loader for a String or an Int is straightforward. The interesting field is authMethods, a List of AuthMethod, where AuthMethod is a sealed trait with three case class implementations.

That is the case that hand written loaders get wrong. The configuration has to say which of the three shapes it means, and the loader has to map the discriminator in the file to the right case class. PureConfig handles it, and the shape it expects is a type field alongside the data, as the application.conf in the README shows.

The other detail is Port, a case class extending AnyVal. That makes it a value class, so it is erased at runtime and carries no allocation cost, and it still has a named type in the configuration. The library treats it as a first class field type, and a type that looks like a single Int to the compiler is still a distinct type to the configuration.

The mechanism is exposed as an import. The generic.auto import is what enables the automatic derivation, and the README also documents ways to add support for custom types or to customize existing ones for cases where the automatic behaviour is not what you want.

## HOCON, JSON and .properties, with a recommendation

PureConfig reads Typesafe Config configurations, and because of that it supports HOCON, JSON and Java .properties formats. HOCON is a superset of both JSON and .properties and is highly recommended.

The recommendation is worth pausing on, because it is the one place where the library tells you to change something rather than just accept what you have. A .properties file cannot express a nested structure, and a JSON file cannot express a configuration with a variable reference in it. HOCON can, because it is a superset of both and adds variable substitution and file sourcing as advanced features.

That last pair matters more than it sounds. Variable substitution means one value can refer to another, and file sourcing means a configuration can include another file. Both are how large configurations avoid becoming a wall of copy-pasted values, and both are unavailable if you settle for .properties.

The formats are handled by Typesafe Config, not by PureConfig, and that division is the key to understanding the library's boundaries. The various loadConfig methods defer to Typesafe Config's ConfigFactory to select where to load the config files from, and the README points at the well-documented rules for configuration loading rather than repeating them, on the grounds that there are subtleties. PureConfig's job starts once Typesafe Config has produced a configuration tree, and its job is turning that tree into your type.

## Precedence, partial files, and the Either you get back

Two details in the quick start are about behaviour rather than syntax, and both are easy to get wrong in a deployment.

The first is file precedence. PureConfig provides a loadConfigFromFiles method that builds a configuration from an explicit list of files, where files earlier in the list have greater precedence than later ones. Each file can include a partial configuration, as long as the whole list produces a complete configuration. The README points at the test of loadConfigFromFiles in ApiSuite.scala for a worked example, which is a sensible place to look, since a configuration layering scheme is exactly the kind of thing that is easier to read as a test than as prose.

The second is the return type. Loading the configuration is a single expression:

```scala
ConfigSource.default.load[ServiceConf]
```

And the result is ConfigReader.Result[ServiceConf], which is just an alias for Either[ConfigReaderFailures, ServiceConf], so you can handle it just like you would handle an Either value.

That alias is a deliberate design choice and it has consequences in both directions. A configuration load does not throw, it returns a failure value that carries the details of what went wrong, which is the right shape for something that is expected to fail on a typo in a production file. The cost is that every call site has to deal with the failure, and a codebase that ignores the left side of the Either is worse than one that caught an exception, because the failure is easy to drop silently. The README's example shows the successful case, and the shape of the type is what tells you the other case exists.

## Scala 2 and Scala 3 need different artifacts

The install instructions differ by Scala version, and the difference is in the artifact and in how the derivation is obtained.

For an existing SBT project with Scala 2.13 or a later version, the dependency is:

```scala
libraryDependencies += "com.github.pureconfig" %% "pureconfig" % "0.17.10"
```

For Scala 3, it is:

```scala
libraryDependencies += "com.github.pureconfig" %% "pureconfig-core" % "0.17.10"
```

The core artifact is the one to reach for on Scala 3, and the README says most of the documentation applies there too, with a specific guide for Scala 3's derivation linked separately. The example build.sbt in the repository is the reference for a complete file.

There is a warning that is easy to skim past. Earlier versions of Scala had bugs which can cause subtle compile-time problems in PureConfig, and as a result the project recommends using only the latest Scala versions within the minor series.

That is a constraint on your build, not on the library, and it is the kind of thing that has to be settled before a project starts, because a Scala minor version is usually pinned by the rest of the build and changing it is not a decision one library should force on you.

The version numbers deserve a second look too. The README's dependency snippet uses 0.17.10, while the badges in the header point at the 2.13 artifacts of a different line, with the Scaladoc badge pointing at pureconfig-core_2.13. So the documentation and the badges are not pointing at the same release, and the version to use is the one in the quick start rather than the one in the badge.

## Conclusion

PureConfig fits a Scala service where configuration is a typed case class and the alternative is a hand written loader per field, especially one with sealed traits and custom types that the compiler can check. It does not fit a project that wants a different configuration language, because the file handling is Typesafe Config's, and it does not fit a team on an old Scala minor version, which the README warns can cause subtle compile-time problems. Before you adopt it, decide which Scala version you are on, because the artifact name and the derivation mechanism both differ between Scala 2 and Scala 3.

## FAQ

### What does PureConfig do?

It is a Scala library that reads Typesafe Config configurations written in HOCON, Java .properties or JSON into native Scala classes in a boilerplate-free way, with sealed traits, case classes, collections and optional values supported out of the box, and it generates the loading code at compile time from the type you define.

### How do I add PureConfig to an SBT project?

For Scala 2.13 or later, add libraryDependencies += "com.github.pureconfig" %% "pureconfig" % "0.17.10" to build.sbt. For Scala 3, add the same line with the artifact named pureconfig-core instead, and read the separate Scala 3 derivation guide.

### What happens when a PureConfig load fails?

The load returns ConfigReader.Result, which is an alias for Either[ConfigReaderFailures, ServiceConf], so the failure carries the details of what went wrong and can be handled like any other Either rather than thrown as an exception.

### How does file precedence work in PureConfig?

The loadConfigFromFiles method builds a configuration from an explicit list of files where files earlier in the list have greater precedence, and each file may contain a partial configuration as long as the whole list produces a complete configuration. The loadConfig methods themselves defer to Typesafe Config's ConfigFactory for where files are loaded from.

### Which configuration formats can PureConfig read?

Because it uses Typesafe Config, it reads HOCON, JSON and Java .properties, and HOCON is a superset of both JSON and .properties and is highly recommended, since it also supports variable substitution and file sourcing.

## Sources

- [Issues](https://github.com/pureconfig/pureconfig/issues)
- [License: MPL-2.0](https://github.com/pureconfig/pureconfig/blob/master/LICENSE)
- [Project website](https://pureconfig.github.io)
- [pureconfig/pureconfig on GitHub](https://github.com/pureconfig/pureconfig)
- [README](https://github.com/pureconfig/pureconfig/blob/master/README.md)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/pureconfig-pureconfig
