Library / SDK
clj-commons/pretty avatar
clj-commons/pretty

Pretty gives Clojure a readable stack trace and a table printer

Library for helping print things prettily, in Clojure - ANSI fonts, formatted exceptions

624 stars29 forksClojureApache-2.0

At a glance

What is it?
org.clj-commons/pretty is a Clojure library for readable output: it replaces the default exception rendering with a highlighted, de-duplicated version, adds ANSI colour support, prints byte sequences and their diffs, and formats tabular data. It plugs into Leiningen, the clj tool and nREPL so you can use it without adding it to your project dependencies.
Who is it for?
Pretty earns a place in the tooling of anyone who reads Clojure stack traces for a living, since the highlighted output and the repetition detection make a failing test or a production exception much faster to read, and the table and byte printers cover the two other console chores it handles well.
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 9 days ago.
What is it written in?
Mainly Clojure, 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

The stack trace that Clojure prints by default

The library opens with a short argument. If you are trying to puzzle out a stack trace, pick a critical line of text out of a long stream of console output, or compare two streams of binary data, formatting can go a long way. The tagline is sometimes, neatness counts, which is about the right level of ambition for a console library.

What Pretty adds is listed in three bullets: readable output for exceptions, general ANSI font and background color support, and readable output for binary sequences.

The exception work is the part that changes daily life for a Clojure developer. The README describes the goal as being able to scan down and see things in chronological order, with the important parts highlighted, names that are the same or closer to the source code, unnecessary details omitted, and file names and line numbers easy to pick out. The example given is an output stream from the Pedestal test suite, shown once without Pretty and once with it, which is a fair demonstration because that output is exactly the kind of long chronological dump the library targets.

The other capability that catches attention is repetition detection. Pretty can identify the parts of a stack trace that repeat, which the README notes is common with Clojure code, and omit the repetition. A deep call chain through the same few functions can produce a trace where the same frames repeat for a dozen lines, and collapsing them is what makes the trace short enough to read.

Turning on pretty exceptions from a REPL

Enabling Pretty exceptions means invoking the function clj-commons.pretty.repl/install-pretty-exceptions. The README is clear about what that does: it redefines a number of Vars to replace the default implementations with prettier ones. That mechanism is the reason the library can be dropped into a REPL session without touching the code under test, and it is also the reason it can surprise you, since it changes global behaviour rather than wrapping a single call site.

The simplest place to put the call is the user.clj namespace of your REPL.

For shared projects, the README suggests a different route. In shared projects many people have different ideas about what is best for error reporting, so the recommendation is to use a Leiningen profile, which keeps the dependency out of the project itself. Create a file at ~/.lein/profiles.d/debug.clj:

clojure
{:dependencies
 [[org.clj-commons/pretty "3.5.0"]]
 :injections
 [(require '[clj-commons.pretty.repl :as repl])
  (repl/install-pretty-exceptions)]}

Then the REPL is started with lein with-profiles +debug, and you get pretty exceptions without adding pretty-specific code or dependencies to the project. The same profile can carry other developer tools, and the README notes that clj-reload is a natural companion for it.

That is the cleanest integration in the whole set, because the change lives entirely in your own machine's profile.

The clj alias and the nREPL middleware route

The clj tool gets an alias. The README shows this configuration:

clojure
{:aliases
 { :debug
  {:extra-deps {org.clj-commons/pretty {:mvn/version "3.5.0"}}
   :main-opts ["--main" "clj-commons.pretty-repl"]}}}

Running clj -M:debug executes clj-commons.pretty-repl/-main, which sets up pretty exceptions before passing any remaining command line arguments to clojure.main/main.

There is a second form for the -X option, which also shows how to run the test runner under Pretty:

clojure
{:aliases
 {:debug
  {:extra-deps {org.clj-commons/pretty {:mvn/version "3.5.0"}}
   :exec-fn clj-commons.pretty.repl/main}
  
  :test
  {:extra-paths ["test"]
   :extra-deps {io.github.cognitect-labs/test-runner
                {:git/tag "v0.5.1" :git/sha "dfb30dd"}}
   :exec-fn cognitect.test-runner.api/test
   :exec-args {:fn cognitect.test-runner.api/test}}}}

The value for :fn must be a fully qualified function name, and clj -X:test:debug overwrites the :exec-fn so that pretty exceptions are set up before delegating to the function named in the :fn option.

The nREPL route is the one the README recommends over the others. Pretty includes an nREPL middleware function, clj-commons.pretty.nrepl/wrap-pretty, that installs pretty exception reporting into the REPL. nREPL is the framework that lets an IDE such as Emacs or Cursive, or a CLI such as Leiningen, interoperate with a running REPL in a subprocess, and the middleware is enabled in project.clj or deps.edn, or in .nrepl/nrepl.edn for editor setups.

The README's verdict on the alternatives is blunt: both the Leiningen and clj approaches are doable but clumsy, so a solution involving nREPL directly will likely be better.

print-table and the table styles it ships

Exceptions are the headline feature, but the table printer is what makes Pretty a general console utility.

The function takes a specification of columns and a collection of rows:

clojure
(def routes
  [{:method     :get
    :path       "/"
    :route-name :root-page}
   {:method     :post
    :path       "/reset"
    :route-name :reset}
   {:method     :get
    :path       "/status"
    :route-name :status}])
    
(print-table
  [:method
   :path
   {:key :route-name :title "Name" :title-align :left}]
  routes)

Columns are keywords, and a column can be a map that overrides the presentation, here giving the route name column the title Name and left alignment. The print-table function has many options to adjust the output, including fonts, text alignment and line annotations, and it supplies several different table styles. One of them is the skinny style:

clojure
(print-table
    {:columns [:method
               :path
               {:key :route-name :title "Name" :title-align :left}]
     :style   table/skinny-style}
    routes)
Method |   Path  | Name      
-------+---------+-----------
  :get |       / | :root-page
 :post |  /reset | :reset    
  :get | /status | :status   
=> nil

The style is chosen with the :style key, so switching between styles is a configuration change rather than a different function call.

Byte sequences, diffs and annotated text

The binary output is inspired by hexyl, and Pretty can print out a sequence of bytes with colour coding. It can also print out a delta of two byte sequences, using background colour to indicate where the two sequences differ.

That delta function is the useful one in practice. Comparing two buffers by eye is hopeless, and a background highlight per byte position turns the comparison into something you can scan in one pass. Whether it is a payload that changed shape between two releases or a response that no longer matches what the server returns, the diff view answers the question directly.

The third capability is annotated text, which the README describes as useful when reporting errors while parsing arbitrary text. An annotation attaches to a span of text and marks it, so a parser can point at the exact region that broke rather than at the whole line.

Taken together with the ANSI font and background color support, these three features cover a recurring category of console work: read this structure, show me what changed, mark this part. The ANSI support is general, so it also applies to your own output rather than only to the library's built-in printers.

Babashka, Clojure versions and where Pretty stops working

Compatibility is stated with a caveat worth reading before you plan to use the library in a script.

Pretty is compatible with Clojure 1.10 and above. That is the floor for the whole library, not just the exception handling.

Babashka is the sharper edge. Parts of Pretty can be used with Babashka, such as the clj-commons.ansi namespace, but Babashka runs in an interpreter and its approach to exceptions is incompatible with JVM exceptions. The practical reading is that the ANSI output helpers travel, and the exception redefinition, which is the main feature, does not. If your Babashka script needs pretty exceptions, this library will not give them to you.

The second limitation is behavioural and applies to every integration route. install-pretty-exceptions redefines a number of Vars, so it changes the meaning of the default implementations process-wide. In a REPL that is exactly what you want. In a shared environment where several people have different ideas about error reporting, and the README says so explicitly, it is a decision that needs to be made deliberately rather than inherited from a shared profile.

The third is the version spread you can see in the release history. The versions move in steps rather than in a smooth sequence, so a project that pins one version and a developer environment on another can see different formatting, which is a good reason to pin the version in the profile or alias rather than tracking the newest release.

Mixed licensing and the shape of an upgrade

The licence is not a single one. The majority of the code is available under the terms of the Apache Software License 1.0, and some portions are available under the terms of the Eclipse Public Licence 1.0. The repository root shows how that is arranged, with a LICENSE-asl.txt file, an epl-v10.html file and an ORIGINATOR file.

For most users this is a non-issue, since both licences are permissive in the sense that they let you use, modify and redistribute the code, and neither obliges you to publish changes to a library you did not modify. It does matter if your organisation has a policy that names specific licences, and it matters if you intend to fork and redistribute parts of the code, because the portions under the Eclipse Public Licence come with their own conditions.

On the release side, the recent versions are 3.6.8 on 2026-03-27, 2.3.0 on 2024-03-09 and 2.0.1 on 2023-07-21. The gap between 2.3.0 and 3.6.8 is wide in version terms and long in calendar terms, which tells you the version number moves when the maintainer decides to move it rather than on a schedule.

The repository is not archived and the last push was on 2026-10-14, so work is still landing on the main branch. The repository is also a Clojure project built with build.clj and a deps.edn, and it carries a VERSION.txt file, a CHANGES.md file, a dev directory, a docs directory and a test directory, so the build and the release history are both in the repository rather than in an external service.

Editorial conclusion

Pretty earns a place in the tooling of anyone who reads Clojure stack traces for a living, since the highlighted output and the repetition detection make a failing test or a production exception much faster to read, and the table and byte printers cover the two other console chores it handles well. It is the wrong choice for a Babashka script that needs exception rendering, since that path is incompatible with JVM exceptions, and for a team that will not accept redefining Vars in a shared REPL. Verify first that your toolchain can carry the middleware, by testing the Leiningen profile or the clj alias in a scratch project, and check that the version you pin matches the Clojure version you run, since Pretty requires Clojure 1.10 or above.

Frequently asked questions

How do I enable pretty exceptions in a Clojure REPL?

Call clj-commons.pretty.repl/install-pretty-exceptions, for example from your user.clj namespace. The function redefines a number of Vars so the default implementations are replaced with prettier ones.

Can I use Pretty without adding it to my project dependencies?

Yes. With Leiningen you can create a profile such as ~/.lein/profiles.d/debug.clj that carries the dependency and an :injections entry, then start the REPL with lein with-profiles +debug. The clj tool offers the same idea through a :debug alias.

Does Pretty work with Babashka?

Partly. The clj-commons.ansi namespace can be used with Babashka, but Babashka runs in an interpreter and its approach to exceptions is incompatible with JVM exceptions, so the exception rendering does not travel.

What can Pretty print besides exceptions?

It can print byte sequences with colour coding inspired by hexyl, a delta of two byte sequences using background colour to mark where they differ, annotated text for reporting errors while parsing, and tabular data through the print-table function with several table styles.

Official sources

  1. clj-commons/pretty on GitHub
  2. License: Apache-2.0
  3. Project website
  4. README
  5. Releases
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.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/clj-commons-pretty.svg)](https://hysenlabs.com/projects/clj-commons-pretty)

Was this useful?