Clojure Ring Hot Reloading HTML in the Browser
Published: 2023-11-26 17:35:18

---
title: Clojure Ring Hot Reloading HTML in the Browser
published: true
date: 2023-08-29 16:40:47 UTC
tags: 
canonical_url: https://blog.tvaisanen.com/clojure-ring-hot-reloading-html-in-the-browser
---

In some cases when you're developing a full-stack app you want to be able to refresh your website when static assets change automatically. For example HTML templates or CSS files.

There's a nice little utility tool [ring-refresh](https://clojars.org/ring-refresh) to achieve just that.

Here's an example app that uses [Selmer](https://github.com/yogthos/Selmer) templates to render HTML. During development we want the browser to update to have a faster feedback loop in the browser.

Here's a small example of how this works!

* * *

Add the dependencies.

```
{:paths ["src" "resources"]
 :deps {org.clojure/clojure {:mvn/version "1.11.1"}
        ring/ring-core {:mvn/version "1.6.3"}
        ring/ring-jetty-adapter {:mvn/version "1.6.3"}
        selmer/selmer {:mvn/version "1.12.59"}
        metosin/reitit {:mvn/version "0.7.0-alpha5"}
        ring-refresh/ring-refresh {:mvn/version "0.1.3"}}}

```

Create a template file `resources/templates/layout.html`. And remember to add the `<head>` tag because the `ring-refresh` will depend on that having in place already. If that's missing the reload will not work because the middleware uses a regex to recognize where to inject the hot reload script. This took me a moment to realize so just a heads up.

```
<!DOCTYPE html>
<html>
    <head>
    </head>
    <body>
        <div>hello {{name}}</div>
        <div>
            <ul>
                {% for item in items %}
                <li><strong>{{item|capitalize}}</strong></li>
                {% endfor %}
            </ul>
        </div>
    </body>
</html>

```

And here's the app. Notice the `selmer.parser/ache-off!` call if you are using extended templates.

> When rendering files Selmer will cache the compiled template. A recompile will be triggered if the last modified timestamp of the file changes. Note that changes in files referenced by the template **will not** trigger a recompile. This means that if your template extends or includes other templates you must touch the file that's being rendered for changes to take effect.
> 
> Alternatively you can turn caching on and off using `(selmer.parser/cache-on!)` and `(selmer.parser/cache-off!)` respectively.
> 
> [https://github.com/yogthos/Selmer#important](https://github.com/yogthos/Selmer#important)

```
(ns acme.app
  (:require [selmer.parser :as selmer]
            [ring.middleware.refresh :as refresh]
            [ring.middleware.params]
            [ring.adapter.jetty :as jetty])
  (:gen-class))

(selmer.parser/cache-off!)

(defn app [_request]
  {:status 200
   :headers {"Content-Type" "text/html"}
   :body (selmer/render-file "templates/layout.html"
                             {:name "World"
                              :items ["one" "two" "three" "four"]})})

(defonce server (atom nil))

(defn start! []
  (reset! server
          (jetty/run-jetty (refresh/wrap-refresh #'app)
                           {:port 8080 :join? false})))

(comment
  (.stop @server)
  (start!))

```

Now when you update your templates the browser should refresh the page automatically and save you a bunch of time and nerves. I hope you found this useful!