Need help with your JSON?

Try our JSON Formatter tool to automatically identify and fix syntax errors in your JSON. JSON Formatter tool

Scala JSON Formatter Libraries and Approaches

If you searched for a Scala JSON formatter, the practical question is usually not just "how do I pretty-print JSON?" but "which Scala library should I use, and what does its formatting API look like today?" The good news is that the mainstream options all handle compact vs. human-readable output well. The better answer depends on whether you are already committed to a JSON stack, whether you need deterministic output for tests and diffs, and whether you are formatting raw JSON strings or Scala values during serialization.

For most teams, the right choice is the JSON library they already use elsewhere in the application. Pretty printing is only one feature. Circe is strong when you want a functional AST plus configurable printers, Play JSON stays simple and familiar inside Play or standalone projects, uPickle/ujson is excellent when you want a lightweight formatter that can reformat raw strings directly, and Jackson fits best when your stack already revolves around Java tooling and ObjectMapper.

Quick Answer

  • Choose Circe if you already use Cats / Typelevel libraries and want the cleanest built-in pretty printer options, including sorted keys and custom printers.
  • Choose Play JSON if you are in the Play ecosystem or want the most straightforward Json.prettyPrint(Json.parse(raw)) workflow.
  • Choose uPickle / ujson if you want a lightweight, current option for reformatting raw JSON strings with indent and sortKeys controls.
  • Choose Jackson Scala Module if your codebase is already Jackson-based or you need mapper and writer-level control beyond a simple pretty-print call.

Comparison at a Glance

LibraryBest FitSimple Pretty PrintWhat Stands Out
CirceFunctional Scala projects that already use Cats or Typelevel libraries.parse(raw).map(_.spaces2)Built-in spaces2, spaces2SortKeys, and configurable Printer.
Play JSONPlay apps or standalone projects that want simple AST-based JSON handling.Json.prettyPrint(Json.parse(raw))Clean API and good ecosystem fit, but less printer customization than Circe or Jackson.
uPickle / ujsonLightweight projects, scripts, CLIs, or tools that mostly need to reformat JSON quickly.ujson.reformat(raw, indent = 2, sortKeys = true)Direct raw-string reformatting, plus indent and sortKeys without much setup.
Jackson Scala ModuleJava-heavy stacks, Spring mixed environments, or codebases centered on ObjectMapper.mapper.writerWithDefaultPrettyPrinter()Strong mapper configuration story and highly customizable writer / pretty-printer behavior.

As of early 2026, official docs for Circe, Play JSON, uPickle, and Jackson Scala Module all show active Scala 3 support. That matters because some older blog posts still frame Scala JSON formatting as mostly a Scala 2 story, which is outdated.

Formatting a Raw JSON String

This is the most useful formatter workflow for logging, debugging, developer tools, and one-off payload inspection. You start with a JSON string, parse it, and then write it back in a pretty-printed form.

Circe

import io.circe.parser.parse

val raw =
  """{"z":1,"a":{"nested":true},"tags":["scala","json"]}"""

val formatted: Either[io.circe.ParsingFailure, String] =
  parse(raw).map(_.spaces2SortKeys)

Circe is especially nice when you want explicit error handling. Parsing returns an Either, so you do not have to rely on exceptions for invalid JSON. If you want compact output instead, use .noSpaces.

Play JSON

import play.api.libs.json.Json

val raw =
  """{"z":1,"a":{"nested":true},"tags":["scala","json"]}"""

val formatted: String =
  Json.prettyPrint(Json.parse(raw))

This is the simplest mental model in the Play ecosystem: parse into a JsValue, then pretty print it. If all you want is readable output and you are already using Play JSON elsewhere, this is often enough.

uPickle / ujson

val raw =
  """{"z":1,"a":{"nested":true},"tags":["scala","json"]}"""

val formatted: String =
  ujson.reformat(raw, indent = 2, sortKeys = true)

This is one of the most practical current approaches when your task is literally reformat this JSON string. You do not need to map to case classes first, and current docs show indent, escapeUnicode, and sortKeys controls directly on reformat.

Jackson Scala Module

import com.fasterxml.jackson.databind.json.JsonMapper

val raw =
  """{"z":1,"a":{"nested":true},"tags":["scala","json"]}"""

val mapper = JsonMapper.builder().build()
val node = mapper.readTree(raw)
val formatted = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(node)

For raw string reformatting, Jackson does not need Scala-specific support yet because you are only working with a JSON tree. Add the Scala module when you serialize Scala collections or case classes through the same mapper.

Formatting Scala Values During Serialization

If your application already has Scala values in memory, pretty printing typically happens after encoding to a JSON AST or during the write call itself.

// Circe
user.asJson.spaces2

// Play JSON
Json.prettyPrint(Json.toJson(user))

// uPickle
write(user, indent = 2, sortKeys = true)

// Jackson
mapper.writerWithDefaultPrettyPrinter().writeValueAsString(user)

In other words, pretty printing is usually a final rendering concern. Your codec or macro derivation choice matters more for application design; the formatting call is usually just one line on top of that.

Deterministic Output and Custom Rules

The moment formatting becomes part of tests, snapshots, cache keys, or human code review, deterministic output matters more than simple readability. Sorted keys and consistent null handling are the first things teams usually care about.

  • Circe: You can use .spaces2SortKeys for a quick stable representation or build a custom Printer when you need sorted keys, a specific indent string, or dropped null fields.
  • uPickle / ujson: Current docs expose sortKeys = true directly, which makes it unusually convenient for test fixtures and JSON diffing.
  • Play JSON: The built-in pretty printer is intentionally straightforward. It is good for readable output, but it is not the first choice when you need lots of printer customization.
  • Jackson: If formatting is part of a broader serialization policy, Jackson gives you the most room to centralize behavior through mapper features and custom pretty printers.

Circe custom printer example

import io.circe.Printer

val printer = Printer(
  indent = "  ",
  dropNullValues = true,
  sortKeys = true
)

val stableJson: String = printer.print(json)

Current Gotchas

  • Do not optimize for pretty printing alone. If your application already depends on Circe, Play JSON, uPickle, or Jackson for codecs and parsing, adding a second JSON library just for formatting is usually wasted complexity.
  • Invalid JSON still has to parse first. A formatter cannot rescue malformed input; it can only re-render valid JSON into a different layout.
  • Older advice about Scala 3 can be stale. Current official docs for these libraries now show solid Scala 3 support, so check the project docs instead of relying on older comparison posts.
  • Jackson 3 changed some Scala guidance. The current Scala module README notes Java 17 as the minimum and points Scala 3 users toward newer APIs such as ClassTagExtensions instead of the old ScalaObjectMapper pattern.

Which Approach Should You Use?

If your main need is format this JSON string for a log, test fixture, or debugging session, ujson and Circe are the most pleasant choices because they make formatting and deterministic key ordering obvious. If you are already inside a Play app, keep things simple and use Json.prettyPrint. If your environment is Jackson-first, use Jackson and centralize formatting policies in the mapper instead of bolting on a second Scala library.

The overall rule is straightforward: choose the library that fits the rest of your JSON pipeline, then use its built-in compact and pretty-print APIs deliberately. Pretty output is a developer-experience feature, not a reason to fragment your serialization stack.

Need help with your JSON?

Try our JSON Formatter tool to automatically identify and fix syntax errors in your JSON. JSON Formatter tool