Skip to main content
ansicode
Clojure

ANSI escape codes in Clojure — \u001b, clansi, io.aviso/pretty

Clojure inherits Java's string-literal rules — only the Unicode escape \u#### works. \e is not supported, \x1b is not supported, \033 is not supported. Write "\u001b[31m" and lowercase the hex digits after \u (Clojure's reader is case-sensitive about Unicode escape forms). (println "\u001b[1;31merror:\u001b[0m permission denied") works on every JVM-hosted Clojure ≥ 1.0 and on ClojureScript (which compiles to JS where "\u001b" is also valid). On macOS, Linux, BSDs, and Windows 10+ with Conhost 1709+ / Windows Terminal, terminals parse ANSI natively. For the idiomatic helper reach for clansi — the minimal classic Clojure ANSI library — (clansi.core/style "error" :red :bold) returns a styled string with the SGR prefix and reset baked in. For Leiningen-grade structured output and coloured stack traces reach for io.aviso/pretty — the library Leiningen itself uses for its own coloured error formatting; io.aviso.ansi/red, io.aviso.ansi/bold-red, plus io.aviso.exception/format-exception for pretty stack traces. For coloured pretty-printing of data structures reach for mvxcvi/puget — the canonical Clojure pretty-printer, used by cider for inspector output and by deps.edn tooling for human-readable EDN; (puget.printer/cprint value) emits ANSI-coloured indented EDN. For Windows VT enablement (pre-Conhost-1709 builds) and JVM-portable terminal capability use jansi via Java interop — (org.fusesource.jansi.AnsiConsole/systemInstall) enables VT on every JVM-capable platform. Capability gating: (some? (System/console)) is the JVM-portable check (returns nil when stdin/stdout is redirected). Pair with (System/getenv "NO_COLOR") for the env-var convention. CIDER nREPL caveat (page's primary SERP differentiator): editor-attached REPLs (CIDER in Emacs, Calva in VSCode, Cursive in IntelliJ) speak the nREPL bencode protocol — they consume the result :value field and render it themselves, not by piping raw stdout. ANSI escapes inside the :value string pass through unchanged but most editor REPLs display them as raw text. Terminal-attached REPLs (lein repl, clj, bb) DO parse ANSI in stdout. The fix when you actually need coloured output in an editor REPL is to print to *err* (which CIDER passes through verbatim so the host terminal renders the colour) or to install cider-nrepl's print-color middleware that maps SGR codes to editor face overlays.

Recommended libraries

  • clansi

    Minimal classic Clojure ANSI library. (clansi.core/style "error" :red :bold) returns a styled string with the SGR prefix and reset baked in. Style keywords: :red/:green/:blue/:yellow/:magenta/:cyan/:white, :bg-red etc. for backgrounds, :bold/:underline/:reverse/:blink/:reset. Zero magic — composes via plain string concatenation. The standard pick when you don't want a heavier library.

  • io.aviso/pretty

    Leiningen-grade structured output — the library lein itself uses for its coloured error formatting. io.aviso.ansi/red, io.aviso.ansi/bold-red, io.aviso.ansi/cyan-bg, etc. — named SGR helpers. (io.aviso.exception/format-exception e) renders a coloured, source-file-aware stack trace far more readable than the default JVM trace. The de-facto canonical answer when you want exception output that doesn't look like a Java enterprise wall of text.

  • mvxcvi/puget

    Canonical Clojure pretty-printer with ANSI colour output. (puget.printer/cprint value) emits ANSI-coloured indented EDN — used by cider for inspector output and by deps.edn tooling. Configurable palette via {:color-scheme {:keyword [:bold :cyan] :string [:green]}}. Switch to plain puget.printer/pprint when stdout isn't a TTY. The right call when you need readable output of nested data structures.

  • jansi (Java interop)

    JVM-portable ANSI library — the Java side of the Clojure stack. (org.fusesource.jansi.AnsiConsole/systemInstall) enables Windows VT mode on pre-Conhost-1709 builds and re-routes System/out through an ANSI-aware wrapper. (org.fusesource.jansi.Ansi/ansi) provides a fluent builder API accessible via Clojure's . interop. Pull in when you need pre-Windows-10-1709 support or are wrapping a Java library that already uses jansi.

Idiomatic patterns

Direct println with \u001b — Java rules, ClojureScript-compatible
;; Clojure inherits Java string-literal rules — only \u####
;; Unicode escapes work. \e / \x1b / \033 are NOT supported.
;; Hex digits after \u must be lowercase: \u001b not \u001B.
;; The same syntax compiles unchanged on ClojureScript.

(ns example.ansi)

(defn -main []
  (println "\u001b[1;31merror:\u001b[0m permission denied")
  (println "\u001b[33mwarn:\u001b[0m deprecated flag")
  (println "\u001b[32mok:\u001b[0m 142 tests passed")

  ;; Truecolor — 38;2;R;G;B
  (println "\u001b[38;2;255;128;0morange truecolor\u001b[0m"))

;; Reusable helper — wraps SGR prefix + reset around text:
(defn esc [sgr text]
  (str "\u001b[" sgr "m" text "\u001b[0m"))

(println (esc "1;31" "FATAL") "server crashed")
(println (esc "32"   "ok:")    "142 tests passed")
(println (esc "38;2;255;128;0" "truecolor orange"))
clansi + io.aviso/pretty — style keywords + coloured exception trace
;; deps.edn:
;;   {:deps {clansi/clansi      {:mvn/version "1.0.0"}
;;           io.aviso/pretty    {:mvn/version "1.4.4"}}}
;;
;; project.clj:
;;   :dependencies [[clansi "1.0.0"]
;;                  [io.aviso/pretty "1.4.4"]]

(require '[clansi.core :as ansi]
         '[io.aviso.ansi :as aansi]
         '[io.aviso.exception :as aex])

;; clansi/style wraps text with named SGR + reset.
;; Keywords compose left-to-right inside one call.
(println (ansi/style "error: " :red :bold) "permission denied")
(println (ansi/style "ok: " :green)        "142 tests passed")
(println (ansi/style " CAUTION " :black :bg-yellow) " brakes wet")

;; io.aviso.ansi — named helpers (same vocabulary Leiningen
;; uses internally for its own coloured output):
(println (aansi/red "error:")        "permission denied")
(println (aansi/bold-red "FATAL")    "server crashed")
(println (aansi/bold-underlined "Section 1"))

;; Pretty-print an exception with coloured stack frames —
;; THE Clojure-idiomatic way to render exceptions to a TTY:
(try
  (/ 1 0)
  (catch ArithmeticException e
    (println (aex/format-exception e))))
mvxcvi/puget — coloured pretty-print of nested data structures
;; deps.edn: {:deps {mvxcvi/puget {:mvn/version "1.3.4"}}}

(require '[puget.printer :as puget])

(def data
  {:users [{:name "alice" :email "[email protected]" :age 30}
           {:name "bob"   :email "[email protected]" :age 25}]
   :counts {:active 42 :inactive 7}
   :timestamp #inst "2026-05-19T10:00:00Z"})

;; cprint = "coloured print" — ANSI-coloured indented EDN
;; (default palette matches CIDER's inspector colours):
(puget/cprint data)

;; cprint-str returns the coloured string instead of printing:
(let [out (puget/cprint-str data)]
  (spit "snapshot.txt" out))

;; Switch to plain pprint when stdout isn't a TTY:
(if (some? (System/console))
  (puget/cprint data)
  (puget/pprint data))

;; Custom palette — override individual SGR codes per type:
(puget/cprint data
              {:print-color true
               :color-scheme {:keyword [:bold :cyan]
                              :string  [:green]
                              :number  [:yellow]
                              :nil     [:bold :black]}})
Capability gate + CIDER nREPL vs lein repl ANSI passthrough caveat
;; Capability gating in Clojure:
;;   (some? (System/console))      — JVM-portable, returns nil
;;                                    when stdin/stdout redirected
;;   (System/getenv "NO_COLOR")    — universal env-var convention
;;   (System/getenv "FORCE_COLOR") — universal opt-in
;;
;; CIDER nREPL CAVEAT (the #1 "my colours don't show up in CIDER"
;; friction point):
;;
;; Terminal-attached REPLs (lein repl, clj, bb) speak raw stdout,
;; so ANSI passes through and the host terminal parses it.
;;
;; Editor-attached REPLs (CIDER in Emacs, Calva in VSCode,
;; Cursive in IntelliJ) speak the nREPL bencode protocol — they
;; consume the result :value field and render it themselves.
;; ANSI bytes inside :value pass through unchanged but most
;; editor REPLs display them as raw text.
;;
;; Fix when you actually need coloured output in an editor REPL:
;;   1) Print to *err* — CIDER passes stderr through verbatim,
;;      so the host terminal (not the editor buffer) renders it.
;;   2) Install cider-nrepl's print-color middleware, which
;;      maps SGR codes to editor face overlays.

(defn ansi-capable? []
  (cond
    (System/getenv "NO_COLOR")    false
    (System/getenv "FORCE_COLOR") true
    :else (some? (System/console))))

(defn styled [text sgr]
  (if (ansi-capable?)
    (str "\u001b[" sgr "m" text "\u001b[0m")
    text))

(println (styled "OK"   "32"))
(println (styled "FAIL" "1;31"))

;; Force-render to the host terminal even when CIDER is the
;; attached REPL — *err* is passed through verbatim:
(binding [*out* *err*]
  (println (styled "DEBUG" "36") "checkpoint hit"))

Related sequences

Other languages