{"id":13850542,"url":"https://github.com/dundalek/clojurescript-guide","last_synced_at":"2026-03-16T12:05:26.173Z","repository":{"id":145313678,"uuid":"91343791","full_name":"dundalek/clojurescript-guide","owner":"dundalek","description":"My notes on ClojureScript","archived":false,"fork":false,"pushed_at":"2021-09-10T18:56:12.000Z","size":9,"stargazers_count":69,"open_issues_count":0,"forks_count":0,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-01-19T13:32:23.472Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dundalek.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2017-05-15T13:54:53.000Z","updated_at":"2023-09-08T17:25:04.000Z","dependencies_parsed_at":"2024-01-18T09:57:57.198Z","dependency_job_id":"a26762b1-44c6-4495-8d03-0c16242374f9","html_url":"https://github.com/dundalek/clojurescript-guide","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dundalek%2Fclojurescript-guide","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dundalek%2Fclojurescript-guide/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dundalek%2Fclojurescript-guide/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dundalek%2Fclojurescript-guide/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dundalek","download_url":"https://codeload.github.com/dundalek/clojurescript-guide/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243293781,"owners_count":20268139,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-04T20:01:17.938Z","updated_at":"2025-12-25T13:19:53.540Z","avatar_url":"https://github.com/dundalek.png","language":null,"readme":"\n# The Missing Guide for ClojureScript\n\nWhen I started with ClojureScript and tried to follow the official [Quick Start Guide](https://clojurescript.org/guides/quick-start) it did not seem very straightforward to me. It is a good read though after a bit of hands on experience. This guide is a compilation of things I wished to know at the beginnings.\n\n\u003e ⚠ **DISCLAIMER**  \n\u003e I wrote down these notes as I was learning Clojure/Script in 2017. The tooling ecosystem changed significantly since then, but I did not manage to update the guide to reflect that. Here is the gist:\n\u003e * [Clojure CLI](https://clojure.org/guides/deps_and_cli) is a better alternative to Leiningen\n\u003e * [shadow-cljs](https://github.com/thheller/shadow-cljs) is the best tool to compile ClojureScript replacing Fighweel, both for web and node.js apps\n\u003e * [Babashka](https://github.com/babashka/babashka) is a best runtime for scripting instead of Lumo\n\n\u003c!-- TOC depthFrom:2 depthTo:3 withLinks:1 updateOnSave:1 orderedList:0 --\u003e\n\n- [Quick Start](#quick-start)\n\t- [Create a project](#create-a-project)\n- [Documentation](#documentation)\n- [Resources](#resources)\n\t- [Tutorials](#tutorials)\n\t- [Books](#books)\n\t- [Exercises](#exercises)\n- [Cookbook](#cookbook)\n\t- [Libraries](#libraries)\n\t- [JS Interop](#js-interop)\n\t- [IO](#io)\n\t- [EDN](#edn)\n- [Editor setup](#editor-setup)\n- [Debugging](#debugging)\n- [Tools](#tools)\n- [Misc](#misc)\n\n\u003c!-- /TOC --\u003e\n\n## Quick Start\n\nTry out some code using REPL.\n\nUnder node.js install [Lumo](https://github.com/anmonteiro/lumo) with:\n```\n$ npm install -g lumo-cljs\n$ lumo\ncljs.user=\u003e (println \"hello clojure\")\nhello clojure\n```\n\nFor Java first install [Leiningen](https://leiningen.org/) and then:\n```\n$ lein repl\nuser=\u003e (+ 1 2)\n3\n```\n\n### Create a project\n\n#### Node.js server or CLI app\n\nUse [cljs-node-app](https://github.com/yanatan16/cljs-node-app-template) template:\n`lein new cljs-node-app \u003cproject-name\u003e`\n\nCompile the project: `lein cljsbuild once main`\n\nWatch and recompile on changes: `lein cljsbuild auto main`\n\n#### Web app\n\nUse [re-frame](https://github.com/Day8/re-frame-template) template which uses React.js via [Reagent](https://github.com/reagent-project/reagent) under the hood:\n`lein new re-frame \u003cproject-name\u003e +test +routes +aliases`\n\nStart development mode with live reload: `lein dev`\n\nCompile the project: `lein build`\n\n## Documentation\n\nInside REPL:\n\n```clojure\n;; Show function docs in REPL\n(doc str)\n\n;; Search in docs\n(find-doc \"reduce\")\n\n;; Show function source code\n(source identity)\n```\n\nBrowse [API documentation](https://clojuredocs.org/quickref) and refer to [Cheatsheet](http://clojure.org/cheatsheet) on the web.\n\n## Resources\n\n- [Official Clojure Reference](https://clojure.org/reference/reader)\n- [Library Coding Standards](https://dev.clojure.org/display/community/Library+Coding+Standards)\n- [Clojure Styleguide](https://github.com/bbatsov/clojure-style-guide)\n- [clojure-doc.org](http://clojure-doc.org/articles/content.html) is community-driven documentation\n- [Anki deck](https://ankiweb.net/shared/info/3248915342) for spaced-repetition learning\n\n### Tutorials\n\n- [Learn Clojure in Y minutes](http://learnxinyminutes.com/docs/clojure/) is a very concise indroduction to Clojure.\n- [ClojureScript Unraveled](http://funcool.github.io/clojurescript-unraveled/) is an online book and guide about ClojureScript.\n- [Brave Clojure – Chapter 3 Crash Course](http://www.braveclojure.com/do-things/)\n  Clojure for the Brave and True is a book available online. I find chapter 3 a very good intro to Clojure. If you like more chatty style and humor in programming books then also give a read to other chapters.\n- [Clojure – Functional Programming for the JVM](http://java.ociweb.com/mark/clojure/article.html) is an introductory article to functional programming and Clojure. It is aimed for Java programmers but contains interesting bits of information and goes into more low-level details.\n- [Clojure from the ground up](https://aphyr.com/tags/Clojure-from-the-ground-up) is a collection of articles on various Clojure topics.\n\n### Books\n\n- [Programming Clojure by Stuart Halloway](https://www.amazon.com/dp/1934356867) is a best book about Clojure for programmers, I recommend anyone learning Clojure(Script) to start with this one.\n- [Clojure Applied: From Practice to Practitioner](https://www.amazon.com/dp/1680500740) is a book focusing on more practical aspects of app development in Clojure. Includes chapters about domain modelling, managing state and application components.\n- [Mastering Clojure](https://www.amazon.com/dp/B017XSFL4Q/) describes in detail more advanced topics like paralelism, transducers, category theory, pattern matching and logic programming.\n\nExplore other [books](https://clojure.org/community/books) by the community.\n\n### Exercises\n\n- [99 Lisp Problems](http://www.ic.unicamp.br/~meidanis/courses/mc336/2006s2/funcional/L-99_Ninety-Nine_Lisp_Problems.html)\n- [4Clojure](http://www.4clojure.com/problems)\n- [Clojure Koans](http://clojurekoans.com/)\n- [Project Euler Problems](https://projecteuler.net/archives)\n- [Simple Programming Problems](https://adriann.github.io/programming_problems.html)\n\n## Cookbook\n\n### Libraries\n\nOpinionated list of useful libraries. There are alternatives but if you are just starting you can't go wrong by picking these.\n\n**Frontend**\n- React wrapper framework: [re-frame](https://github.com/Day8/re-frame)\n- UI Components: [re-com](https://github.com/Day8/re-com)\n- Forms with validation: [Reforms](https://github.com/bilus/reforms)\n- HTTP requests: [cljs-ajax](https://github.com/JulianBirch/cljs-ajax)\n- Routing: [Secretary](https://github.com/gf3/secretary)\n- Date, Url and other utilities: [Google Closure Library](https://google.github.io/closure-library/api/goog.html)\n\nCheck other libraries in [Awesome ClojureScript list](https://github.com/hantuzun/awesome-clojurescript).\n\n**Backend**\n- HTTP APIs: [Compojure](https://github.com/metosin/compojure-api)\n- Logging: [Timbre](https://github.com/ptaoussanis/timbre)\n\n### JS Interop\n\nA great feature of ClojureScript is that you can work with native JS objects and use existing JS libraries.\n\n```clojure\n;; Use js/ prefix for global names, e.g. to print in browser console\n(js/console.log \"Hello, world!\")\n\n;; Add dot to construct new objects\n(js/Date. \"2017-10-16\") ; new Date(\"2017-10-16\")\n\n;; Call methods\n(.toUpperCase s) ; s.toUpperCase()\n\n;; Use treading macro to chain calls\n(-\u003e \"BOOM\" (.toLowerCase) (.slice 0 -1)) ; \"BOOM\".toLowerCase().slice(0, -1)\n\n;; Access attributes\n(.-length s) ; s.length\n\n;; Nested attribute access\n(.-location.href js/window) ; window.location.href\n(aget window \"location\" \"href\") ; window[\"location\"][\"href\"]\n\n;; Set attributes\n(aset obj \"attr\" \"val\") ; obj[\"attr\"] = \"val\"\n\n;; create native js objects with #js macro\n#js {:a 1 :b 2} ; {a: 1, b: 2}\n#js [1 2 3] ; [1, 2, 3]\n\n;; convert cljs data structures into js objects\n(clj-\u003ejs obj)\n\n;; convert js objects to cljs data structures\n(js-\u003eclj obj :keywordize-keys true))\n```\n\nSee a [post with more details](http://www.spacjer.com/blog/2014/09/12/clojurescript-javascript-interop/) and [comparison of JS and Clojure](https://kanaka.github.io/clojurescript/web/synonym.html) idioms.\n\n\nUse node modules by [seamlessly requiring](https://clojurescript.org/news/2017-07-12-clojurescript-is-not-an-island-integrating-node-modules) them (starting with 1.9.854):\n```clojure\n(ns example.core\n  (:require [react :refer [createElement]]\n            [\"react-dom/server\" :as ReactDOMServer :refer [renderToString]]))\n\n(js/console.log (renderToString (createElement \"div\" nil \"Hello World!\")))\n```\n\nSome js libraries do not work with the compiler. In that case it is possible to bundle them with  [webpack](https://github.com/roman01la/cljs-reagent-webpack).\nOr use externs: https://code.thheller.com/blog/shadow-cljs/2017/10/15/externs-the-bane-of-every-release-build.html\nDebug advanced compilation errors with [:pseudo-names](https://clojurescript.org/reference/compiler-options#pseudo-names). Also try [:infer-externs](https://clojurescript.org/reference/compiler-options#infer-externs).\n\n### IO\n\nTo print at ClojureScript REPL\n```clojure\n(println \"Hello, world!\")\n```\n\nWorking with filesystem – require `fs` from node and call native functions.\n```clojure\n(def fs (js/require \"fs\"))\n(fs.readFileSync \"foo.txt\" \"utf8\")\n```\n\nWhen working in repl you can use `slurp` to read a file and `spit` to write a file.\n```clojure\n;; Read a file into one long string\n(def a-long-string (slurp \"foo.txt\"))\n\n;; Write a long string out to a new file\n(spit \"foo.txt\"\n\"A long\nmulti-line string.\nBye.\")\n```\n\nAlternative approach is to use [cljs-node-io](https://github.com/pkpkpk/cljs-node-io) which is a port of [clojure.java.io](http://clojure-doc.org/articles/cookbooks/files_and_directories.html).\n\n### EDN\n\nEDN is to Clojure what JSON is to Javascript.\n\n- parse: [cljs.reader/read-string](https://cljs.github.io/api/cljs.reader/read-string)\n- stringify: [prn-str](http://cljs.github.io/api/cljs.core/prn-str)\n\n### State\n\nA state is the value of an identity at a point in time.\n\nChanges to shared state:\n- Refs - coordinated synchronous\n- Atoms - uncoordinated synchronous\n- Agents - asynchronous\n- Vars - thread-local private - def, defn\n\nConvention dynamic thread-wide bindings with asterisks `*in*` `*out*`.\n\n## Editor setup\n\n**[Atom](https://atom.io)**\n\nRefer to [this guide](https://gist.github.com/jasongilman/d1f70507bed021b48625) for a good setup. Essential packages are:\n- [parinfer](https://atom.io/packages/parinfer) so that you don't have to worry about parentheses\n- [proto-repl](https://atom.io/packages/proto-repl) to evaluate code right within editor window\n\n**[Spacemacs](http://spacemacs.org/)**\n- Emacs distribution with Vim mode\n- Looks intriguing, it's on my list of things to try\n- Plugin for code refactoring: [clj-refactor](https://github.com/clojure-emacs/clj-refactor.el/wiki)\n\n**[Cursive](https://cursive-ide.com/)**\n- a great IDE based on IntelliJ\n\n\n## Debugging\n\n**Debug with Chrome DevTools**\n\n- [Enable custom formatters](https://github.com/binaryage/cljs-devtools/blob/master/docs/installation.md#enable-custom-formatters-in-chrome)\n- Data formating is done with [cljs-devtools](https://github.com/binaryage/cljs-devtools) which is already included in the [web app template](#web-app) mentioned above\n- Add expressions to watches like: `cljs.core.pr_str(localvar)`\n\nFor more functionality use [Dirac](https://github.com/binaryage/dirac), a DevTools fork with additional ClojureScript related features.\n\n**Debug node apps with Chrome DevTools**\n- Run `node --inspect-brk build/main.js`\n- In Chrome open `chrome://inspect` and select the session\n\n**Call tracing inside Atom**\n\nUse `proto-repl: save call` to record function calls and values of parameters, watch [video with instructions](https://youtu.be/buPPGxOnBnk?t=11m55s).\n\n**General call tracing**\n\nTrace calls with [Clairvoyant](https://github.com/spellhouse/clairvoyant) which is an alternative to  [clojure.tools.trace](https://github.com/clojure/tools.trace).\n\n## Tools\n\nTo use Leiningen plugins globally across projects put them in `~/.lein/profiles.clj`, e.g.\n```clojure\n{:user {:plugins [[lein-ancient \"0.6.5\"]\n                  [lein-plz \"0.4.0-SNAPSHOT\" :exclusions [[rewrite-clj] [ancient-clj]]]]}}\n```\n\n[List of Leiningen plugins](https://github.com/technomancy/leiningen/wiki/Plugins)\n\n**Packages management**\n- `lein search \u003cpkg-name\u003e` – Search for packages\n- `lein plz add \u003cpkg-name\u003e` – Add package as dependency into `project.clj`\n- `lein ancient` – List outdated dependencies\n- `lein ancient upgrade`  –  Upgrade outdated dependencies\n- [lein try](https://github.com/rkneufeld/lein-try) – Try out Clojure libraries in a REPL without creating a project or adding them to an existing project.\n\n**Code quality**\n- [kibit](https://github.com/jonase/kibit) – static analysis tool that offers suggestions for code improvement\n- [cljfmt](https://github.com/weavejester/cljfmt) or [lein-zprint](https://github.com/kkinnear/lein-zprint) or  [boot-fmt](https://github.com/pesterhazy/boot-fmt) for code auto-formatting\n- [cloverage](https://github.com/cloverage/cloverage) – Test code coverage tool\n- [vanity](https://github.com/dgtized/lein-vanity) - Compute Lines of code statistics\n- [Overview of code quality tools](https://blog.jeaye.com/2017/08/31/clojure-code-quality/)\n\n**Code exploration**\n- [lein-ns-dep-graph](https://github.com/hilverd/lein-ns-dep-graph) or [lein-hiera](https://github.com/greglook/lein-hiera) – Explore and visualize namespace dependencies\n- [lein-gossip](https://github.com/actsasgeek/lein-gossip) or [clj-usage-graph](https://github.com/gfredericks/clj-usage-graph) – Visualize call-graphs in a codebase\n- [lein-instant-cheatsheet](https://github.com/cammsaul/lein-instant-cheatsheet) – Instant Cheatsheet instantly creates a cheatsheet for your project and its dependencies\n\n**Documentation generators**\n\nRecommended:\n- [cljdoc](https://cljdoc.xyz/) – documentation generator, includes hosting\n- [codeina](https://github.com/funcool/codeina) – api doc generator which is a nicer looking fork of codox\n- [codox](https://github.com/weavejester/codox) – with support for plugins and themes\n  - [codox-md](https://github.com/hugoduncan/codox-md)\n- [marginalia](https://github.com/gdeer81/lein-marginalia)  – documentation showing description and code side by side in a literate programming style\n\nOthers:\n- [autodoc](https://github.com/tomfaulhaber/lein-autodoc) – generator that is used for official Clojure API docs\n- [cadastre](https://github.com/dakrone/cadastre) – extracts metadata in a way that is used on [clojuredocs.org](http://clojuredocs.org/)\n\n## Misc\n\n[CrossClj](https://crossclj.info/) – Explore dependencies of Clojure packages among each other, see which function are called where and with what arguments:\n\n[JavaScript to ClojureScript translator](https://github.com/roman01la/javascript-to-clojurescript)\n\n\nBootstrapping:\n- [calvin](https://github.com/eginez/calvin) – A minimalistic build tool for ClojureScript projects that does not require the JVM\n- [with Lumo](https://anmonteiro.com/2017/02/compiling-clojurescript-projects-without-the-jvm/)\n\nArticles about Lisp and Functional Programming:\n- https://gist.github.com/reborg/dc8b0c96c397a56668905e2767fd697f\n- http://www.paulgraham.com/icad.html\n- https://funcall.blogspot.cz/2009/03/not-lisp-again.html\n- http://calculist.org/blog/2012/04/17/homoiconicity-isnt-the-point/\n- http://www.michaelnielsen.org/ddi/lisp-as-the-maxwells-equations-of-software/\n- http://www.lihaoyi.com/post/WhatsFunctionalProgrammingAllAbout.html\n- http://www.cs.umd.edu/~nau/cmsc421/norvig-lisp-style.pdf\n","funding_links":[],"categories":["Others"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdundalek%2Fclojurescript-guide","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdundalek%2Fclojurescript-guide","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdundalek%2Fclojurescript-guide/lists"}