Local-First Notebooks for Clojure

👋 Thanks a lot for helping test Clerk before its open source release.

If you run into any issues or have ideas on how Clerk could be improved, please reach out in #nextjournal on the Clojurians slack or @mkvlr on twitter 🙏

🐦 If you build something interesting with Clerk, tweeting about it is much appreciated.

Clerk takes a Clojure namespace and turns it into a notebook:


Computational notebooks allow arguing from evidence by mixing prose with executable code. For a good overview of problems users encounter in traditional notebooks like Jupyter, see I don't like notebooks and What’s Wrong with Computational Notebooks? Pain Points, Needs, and Design Opportunities. Specifically Clerk wants to address the following problems:

  • Less helpful than my editor

  • Notebook code being hard to reuse

  • Reproduction problems coming from out-of-order execution

  • Problems with archival and putting notebooks in source control

Clerk is a notebook library for Clojure that aims to address these problems by doing less, namely:

  • no editing environment, folks can keep using the editors they know and love

  • no new format: Clerk notebooks are regular Clojure namespaces (interspersed with markdown comments). This also means Clerk notebooks are meant to be stored in source control.

  • no out-of-order execution: Clerk notebooks always evaluate from top to bottom. Clerk builds a dependency graph of Clojure vars and only recomputes the needed changes to keep the feedback loop fast.

  • no external process: Clerk runs inside your Clojure process, giving Clerk access to all code on the classpath.

Using Clerk


The fastest way to get started with Clerk and see what it's capable of is by cloning the Clerk demo repository and running the code in user.clj.

git clone

See the /notebooks folder in the for a number of sample notebooks.

In Your Project

To use Clerk in your project, add the following dependency to your deps.edn:

{:deps {io.github.nextjournal/clerk {:mvn/version "0.1.164"}}}
Extensible Data Notation

Require and start Clerk as part of your system start, e.g. in user.clj:

  '[nextjournal.clerk.webserver :as webserver]
  '[nextjournal.clerk :as clerk]
  '[nextjournal.beholder :as beholder])
(webserver/start! {:port 7777})
;; optionally start a file-watcher to automatically refresh notebooks when saved
(def filewatcher
  (beholder/watch #(clerk/file-event %) "notebooks" "src"))
;; or call `clerk/show!` explicitly
(clerk/show! "notebooks/test.clj")

You can then access Clerk at http://localhost:7777.



  • Fix lazy loading for non-root elements

  • Fix exception when lazy loading end of string

  • Fix regression in clerk/clear-cache!


  • Replace datoteka/fs with babashka/fs for windows compatability

  • Bump rewrite-clj for windows compatability

    See clj-commons/rewrite-clj#93

Thanks to Jack Coady, Ales Najmann and Manoj Waikar for the reports, Lee Read suggesting a solution & Michiel Borkent for helping testing.


  • Support macros that expand to requires

    Make detection much more solid by checking deps of the analyzed form.

  • Better function viewer showing name

  • Fix display of false results

  • Fix view when result is a function

  • Add reader for object tag

  • Fix live reload in dev by using different dom id for static build

  • Fix map entry display for deeply nested maps

    By making map viewer pass down modified options that selects the map-entry? viewer.

  • Show plain edn for unreadable results

  • Fix showing maps & sets with inhomogeneous types

    Implement a resilient sorting that falls back to ranking according to     default viewer predicates. This is heavily inspired by code from Thomas Heller in shadow-cljs.    

  • Add viewport meta tag to fix layout on mobile devices

Thanks to Sam Ritchie, Carsten Behring and Daniel Slutsky for the reports, and Thomas Heller for his solution in shadow-cljs.


Initial preview release.