How to Build a Markdown Preview App With ClojureScript
Tags: clojurescript, react, learning, tutorial
Published: 2019-09-13 12:07:20

---
title: How to Build a Markdown Preview App With ClojureScript
published: true
description: How to build a simple React app with ClojureScript and Reagent
tags: ClojureScript, react, learning, tutorial
canonical_url: https://www.rockyourcode.com/how-to-build-a-markdown-preview-app-with-reagent
---

_Please note: This is an older post I published a while ago on my blog. If you encounter any problems, hit me up and I'll try to fix them._

_**This app was written before we had React hooks.**_

## Why ClojureScript?

With React, you build small components and combine them. You design _from_ data and then flow it through functions (and React classes).

You start with the programming logic. You then add your UI with HTML-like syntax (JSX).

I enjoy the data-centric approach.

It also powers the core of Clojure and ClojureScript.

I hate Javascript's verbose syntax. Don't get me started on the object model and the pitfalls of `this`. Code breaks because you forgot to make sure to bind your functions correctly.

Thinking in ClojureScript frees you from some of the disaster of mutability. You know that all values are immutable per default.  
So what exactly has to change in the program? And that thing has to be an `atom`.

With Javascript/React I sometimes confuse what exactly can change.  
Which functions should be pure? Use a Stateless Functional Component.  
Which components change state? Use a React class.

ClojureScript and [Reagent](https://reagent-project.github.io/), the React wrapper for ClojureScript, differentiate between mutable state and immutable data.

Their concise language and markup syntax are easier to read. Less noise lowers the barrier to understanding the code.

# Build Something

The [Markdown preview app](https://markdown-preview-reagent.firebaseapp.com/) has a text area where you can type in text and a live preview that shows how this text converts to HTML.

Let's define the state:

```cljs
(ns mdpreview.state
  (:require [reagent.core :refer [atom]]))

(def inital-value                                ; (B)
  "## Welcome to Markdown Preview!
Type in some [Markdown text](https://daringfireball.net/projects/markdown/), e.g. in *italic*.
#### About this site
> Markdown Preview was built with Clojurescript and Reagent.
Documentation and more info for this site is available on **[Github](https://github.com/sophiabrandt/markdown-preview)**.
")

(defonce app-state (atom {:value inital-value})) ; (A)
```

The `app-state`, a [Reagent atom](https://github.com/reagent-project/reagent/blob/master/doc/ManagingState.md) _(A_), is a hash-map with the key `:value` and a value of string _(B)_.

Now the UI:

```cljs
(ns mdpreview.views
  (:require [mdpreview.state :refer [app-state]]  ; (A)
            [mdpreview.events :refer [update-preview, clear-textarea]]
            ["react-markdown" :as ReactMarkdown]))

(defn header
  []
  [:div
   [:h1 "Markdown Preview"]])

(defn textarea
  []
  (let [text (:value @app-state)]           ; (B)
    [:div
     [:textarea
      {:placeholder text
       :value text
       :on-focus #(clear-textarea %)        ; (C)
       :on-change #(update-preview %)}]]))

(defn preview
  []
  [:div
   [:> ReactMarkdown {:source (:value @app-state)}]]) ; (F)


(defn app []
  [:div
   [header]
   [textarea]
   [preview]])

```

```cljs
(ns mdpreview.events
 (:require [mdpreview.state :refer [app-state]]))

(defn clear-textarea [event]               ; (D)
 (.preventDefault event)
 (reset! app-state nil))

(defn update-preview [event]               ; (E)
 (.preventDefault event)
 (swap! app-state assoc :value (.. event -target -value)))

```

The view has four areas:

- a simple `H1` tag with the title (header)
- a component with the text area which also contains the event handlers (textarea)
- a component that converts everything from the text area to HTML (preview)
- the final component combines the sub-components (app)

views.cljs imports `app-state` from state.cljs. We stored the Event handler functions in a separate file (see _(A)_).

In the text area we set up a `let` binding to `text` where we dereference our `app-state`. Dereferencing (the @-symbol) means that we [get the value](http://cljs.github.io/api/syntax/#deref) of the `app-state` atom. Reagent will always re-render a component when any part of that atom is updated (see _(B)_).

We use `text` as the placeholder and the value for this input field. When the user triggers the [Synthetic events](https://reactjs.org/docs/events.html) `onFocus` or `onChange`, the functions from the events.cljs-file change the content (see _(C)_).

`on-focus` (in Hiccup we use kebap-case instead of camelCase) wipes the text area (and the state) clean with a `reset!` (see _(D)_).  
`on-Change` takes the event target value and updates the state. Whenever we type into the text area, we update the value of the `app-state` atom with `swap!` (see _(E)_).

The preview component then takes the `app-state` and takes advantage of the (Javascript) "react-markdown" library. [React Markdown](https://github.com/rexxars/react-markdown) creates a pure React Component. We use `reagent/adapt-react-class` (the `[:>]` syntax) to [employ the React component with Reagent](https://github.com/reagent-project/reagent/blob/master/doc/InteropWithReact.md#creating-reagent-components-from-react-components) (see _(F)_).

```cljs
ns mdpreview.core
  (:require [reagent.core :as r]
            [mdpreview.views :as views]))

(defn ^:dev/after-load start
  []
  (r/render [views/app]
            (.getElementById js/document "app")))

(defn ^:export main
  []
  (start))
```

Finally, core.cljs renders the app and uses [shadow-cljs](http://shadow-cljs.org/) to compile the ClojureScript code.

And that's the whole app.

The code is available on [Github](https://github.com/sophiabrandt/markdown-preview). I've deployed the [live demo to firebase](https://markdown-preview-reagent.firebaseapp.com/).