Clojure Civitas
  • About
  • Posts
  • Authors
  • Explorer

I’ll take a side of REPL with that

Selective interaction with a persistent REPL sidebar inside your Clay documents
Author
Affiliation

Timothy Pratley

Hummi

Published

August 5, 2025

“Would you like to make that interactive?”

– The eternal question facing documentation writers.

Most code documentation has either static code with fixed results or dynamic editor blocks with evaluated results. This post explores a third option, a sidebar REPL that coexists with static examples, letting you copy interesting snippets and build on them.

Persistent Workspace Benefits

The sidebar REPL mirrors the intuitive “one environment, one REPL” model, keeping state across interactions.

(kind/hiccup
  '((require '[reagent.core :as r])
    (defonce state (r/atom {:zaniness "moderate"
                            :mood "curious"}))))

We start with some initial mood and zaniness. Here’s a suggestion to modify it:

(swap! state assoc :zaniness "maximum")
Tip

Click the “copy” icon in the code example to transfer it to the REPL window.

And now we’ll add to the post a mini-app for monitoring the state.

(kind/hiccup
  [:div.card {:style {:margin "20px 0"
                      :max-width "600px"}}
   [:div.card-header.bg-success.text-white
    [:h5.mb-0 "🔍 State Monitor"]]
   [:div.card-body
    [:dl.row.mb-0
     [:dt.col-sm-3.text-end "Current state:"]
     [:dd.col-sm-9
      [:div.p-2.bg-light.border.rounded.font-monospace.small
       ['(fn [] (pr-str @state))]]]
     [:dt.col-sm-3.text-end "Instructions:"]
     [:dd.col-sm-9.text-muted.small
      "Try: " [:div.sourceCode [:code "(swap! state assoc :key \"value\")"]] " in the REPL"]]]])
🔍 State Monitor
Current state:
Instructions:
Try:
(swap! state assoc :key "value")
in the REPL

The state you create in the REPL affects page components.

Curated and Open Exploration

Authors can provide focused examples for specific learning goals, while readers can pursue tangents. Here’s an example showcasing Clojure’s frequencies function with some delicious data:

(frequencies ["apple"
              "banana"
              "apple"
              "cherry"
              "banana"
              "apple"])
{"apple" 3, "banana" 2, "cherry" 1}

Try it with different data, maybe the letters in your name.

(frequencies "supercalifragilisticexpialidocious")

Or simulate random selections:

(frequencies (repeatedly 1000
                         #(rand-nth [:a :b :c :d :e :f])))

Visual Playground

The REPL can affect components on the page. This interactive canvas demonstrates how data can drive visual elements.

(kind/hiccup
  [:div.card {:style {:margin "20px 0"}}
   [:div.card-header.bg-primary.text-white
    [:h5.mb-0 "🎨 Interactive Canvas"]]
   [:div.card-body
    [:p.mb-3 "Shapes controlled by " [:code "@state"] " — use the REPL to create and modify them."]
    ['(fn []
       [:svg {:xmlns "http://www.w3.org/2000/svg"
              :width "100%"
              :height "250px"
              :viewBox "0 0 500 250"
              :style {:border "2px solid #dee2e6" :border-radius "4px" :background "#f8f9fa"}}
        ;; Grid pattern
        [:defs
         [:pattern {:id "grid" :width "50" :height "50" :patternUnits "userSpaceOnUse"}
          [:path {:d "M 50 0 L 0 0 0 50" :fill "none" :stroke "#e9ecef" :stroke-width "1"}]]]
        [:rect {:width "500" :height "250" :fill "url(#grid)"}]
        ;; Dynamic shapes from state
        (for [{:keys [type x y size color]} (:shapes @state)]
           (case type
             :circle [:circle {:cx x :cy y :r size :fill color :stroke "#333" :stroke-width 1}]
             :square [:rect {:x (- x size) :y (- y size)
                             :width (* 2 size) :height (* 2 size)
                             :fill color :stroke "#333" :stroke-width 1}]
             :triangle [:polygon {:points (str x "," (- y size) " "
                                               (- x size) "," (+ y size) " "
                                               (+ x size) "," (+ y size))
                                  :fill color :stroke "#333" :stroke-width 1}]
             nil))])]]])
🎨 Interactive Canvas

Shapes controlled by @state — use the REPL to create and modify them.

Add a single shape (maybe a tiny orange dot? 🟠):

(swap! state update :shapes conj
  {:type :circle :x 250 :y 200 :size 15 :color "#f39c12"})
Tip

Click the “copy” icon in the code example to transfer it to the REPL window, then press CTRL+Enter to eval.

Add more shapes by modifying the :shapes vector:

(swap! state update :shapes into
  [{:type :circle :x 100 :y 100 :size 30 :color "#e74c3c"}
   {:type :square :x 200 :y 150 :size 25 :color "#3498db"}
   {:type :triangle :x 350 :y 100 :size 40 :color "#2ecc71"}])

Generate a ✨random constellation✨:

(swap! state assoc :shapes
  (repeatedly 15
    #(hash-map :type (rand-nth [:circle :square :triangle])
               :x (rand-int 500)
               :y (rand-int 250)
               :size (+ 8 (rand-int 25))
               :color (rand-nth ["#e74c3c" "#3498db" "#2ecc71"
                                 "#f39c12" "#9b59b6" "#1abc9c"]))))

The Full Development Experience

While the sidebar REPL provides convenient experimentation for simple examples, the real power of Clay is in working with the source code. Clone the repository and open it in your editor to get the full interactive notebook experience. You can modify examples, create new namespaces, and contribute ideas back to the community. Add your own namespace under src/ and it is published as a new post.

Sidebar REPL Usage

ClojureCivitas authors can require this namespace and enable the REPL sidebar:

(require '[civitas.repl :as repl])
(repl/scittle-sidebar)

Set :page-layout :full in the :quarto metadata to make best use of the available page space.

I’ll have the static examples with a side of REPL, thanks

Wrapping Up

I hope you enjoyed exploring this sidebar REPL approach. The sidebar REPL complements Clay’s strength of showing examples and results. Authors can intersperse static examples with outputs and interactive prompts for experimentation. The real magic happens when you clone this repository and work with the source code directly.

Ready to contribute? Add your own namespace under src/ and it becomes a new post automatically.

Join the conversation: The best place to discuss this feature, share improvements, or ask questions is the Zulip: clay-dev channel

Give it a try and let us know what you think! 🚀

source: src/civitas/repl.clj