Building Browser-Native Presentations with Scittle

Create interactive presentation systems without build tools using Scittle and Reagent
Author
Published

November 4, 2025

Keywords

scittle, browser, presentations, hot-reload

About This Project

This presentation system was born out of a practical need while preparing technical talks for the Clojure community. Like many developers, I found myself building various tools and demos to showcase Clojure/ClojureScript concepts effectively. During this process, I discovered that Scittle’s browser-native approach could eliminate the typical build complexity that often holds people back from creating interactive presentations.

What started as a personal tool for my own presentations evolved into something I felt compelled to share. The Clojure community has given me so much through open-source libraries, helpful discussions, and shared knowledge. This project is my way of contributing back - offering a simple, accessible way for anyone to create beautiful, interactive presentations without fighting with build tools.

Whether you’re preparing for a conference talk, a meetup presentation, or just want to create interactive documentation, I hope these examples inspire you and save you the hours I spent figuring out the details. The source code is fully available for you to learn from, adapt, and improve upon.

Let’s dive into why this approach matters and how you can use it.

Building Browser-Native Presentations with Scittle

The Problem

You want to create a technical presentation with live code examples. Traditional ClojureScript requires: - shadow-cljs or Figwheel configuration - Build tool setup and dependencies - Webpack or bundler configuration - Minutes of compile time on every change - Complex deployment process

What if you could skip all that and just write ClojureScript in an HTML file? No build step. No configuration. Just pure browser-native code.

That’s Scittle.

What is Scittle?

Scittle is a ClojureScript interpreter that runs entirely in the browser. Created by Michiel Borkent (the genius behind Babashka), it brings the simplicity of scripting to ClojureScript development.

Think of it like this:

JavaScript has script tags that run immediately. Python has interactive notebooks. Clojure has the REPL. Scittle gives ClojureScript the same instant, no-build experience.

Write code, refresh browser, see results. No compilation. No bundling. Pure interpretation.

The Simplest Presentation

(ns scittle.presentations.minimal
  (:require
   [reagent.core :as r]
   [reagent.dom :as rdom]))

(defonce current-slide (r/atom 0))

(def slides
  [{:title "Welcome to Scittle"
    :content "Build presentations without build tools"
    :bg "linear-gradient(to bottom right, #3b82f6, #9333ea)"}
   {:title "No Compilation Required"
    :content "ClojureScript runs directly in the browser"
    :bg "linear-gradient(to bottom right, #10b981, #14b8a6)"}
   {:title "Full Reagent Power"
    :content "Interactive components with React"
    :bg "linear-gradient(to bottom right, #f97316, #dc2626)"}
   {:title "Instant Feedback"
    :content "Changes appear immediately"
    :bg "linear-gradient(to bottom right, #ec4899, #f43f5e)"}])

(defn presentation
  []
  (let [{:keys [title content bg]} (get slides @current-slide)
        at-start? (zero? @current-slide)
        at-end? (= @current-slide (dec (count slides)))]
    [:div.flex.flex-col.h-full
     ;; Gradient content area
     [:div.flex-1.flex.flex-col.justify-center.p-6.rounded-lg
      {:style {:background bg}}
      [:div.text-center.text-white
       [:h1.text-4xl.font-bold.mb-3 title]
       [:p.text-xl.opacity-90 content]]]

     ;; Navigation centered below
     [:div.mt-4
      [:div.text-center.mb-3
       [:span.text-base.font-semibold
        (str (inc @current-slide) " / " (count slides))]]

      [:div.flex.gap-2.mx-auto {:style {:width "fit-content"}}
       [:button.btn.btn-sm.btn-primary
        {:on-click #(swap! current-slide dec)
         :disabled at-start?
         :class (when at-start? "btn-disabled")}
        "← Previous"]

       [:button.btn.btn-sm.btn-primary
        {:on-click #(swap! current-slide inc)
         :disabled at-end?
         :class (when at-end? "btn-disabled")}
        "Next →"]]]]))

(rdom/render [presentation] (js/document.getElementById "minimal-demo"))

See It Live

(kind/hiccup
 [:div#minimal-demo {:style {:min-height "500px"}}
  [:script {:type "application/x-scittle"
            :src "minimal.cljs"}]])

How It Works

The flow is straightforward: 1. Load Scittle.js from a CDN 2. Load the Reagent plugin 3. Write ClojureScript in a script tag with type=“application/x-scittle” 4. Scittle interprets and executes the code 5. Reagent renders React components 6. Everything updates live in the browser!

Visually, the architecture looks like this:

(kind/mermaid
 "flowchart LR
    A[HTML] --> B[Scittle]
    B --> C[Reagent]
    C --> D[DOM]")
flowchart LR A[HTML] --> B[Scittle] B --> C[Reagent] C --> D[DOM]

Key insight: Scittle evaluates ClojureScript directly in the browser. No compilation. No bundling. Just interpretation.

Adding Keyboard Navigation

(ns scittle.presentations.keyboard-nav
  (:require
   [reagent.core :as r]
   [reagent.dom :as rdom]))

(defonce current-slide (r/atom 0))

(def slides
  [{:title "Keyboard Navigation"
    :subtitle "Professional Presentation Control"
    :content "Use arrow keys to navigate through slides smoothly"
    :bg "linear-gradient(to bottom right, #3b82f6, #9333ea)"
    :layout :intro}
   {:title "Arrow Key Controls"
    :content "Simple keyboard shortcuts for navigation"
    :bg "linear-gradient(to bottom right, #10b981, #14b8a6)"
    :layout :keys
    :keys [{:key "←" :action "Previous slide"}
           {:key "→" :action "Next slide"}
           {:key "Home" :action "First slide"}
           {:key "End" :action "Last slide"}]}
   {:title "Implementation"
    :subtitle "Just a few lines of code"
    :bg "linear-gradient(to bottom right, #f97316, #dc2626)"
    :layout :features
    :features ["Single event listener"
               "Works with Scittle out of the box"
               "No dependencies needed"
               "Instant hot reload"]}
   {:title "Ready to Build?"
    :subtitle "Start creating interactive presentations today"
    :bg "linear-gradient(to bottom right, #ec4899, #f43f5e)"
    :layout :summary
    :points ["✓ No build step"
             "✓ No compilation"
             "✓ Pure browser magic"]}])

(defn handle-keydown
  [e]
  (let [key (.-key e)
        max-idx (dec (count slides))]
    (case key
      "ArrowRight" (swap! current-slide #(min max-idx (inc %)))
      "ArrowLeft" (swap! current-slide #(max 0 (dec %)))
      "Home" (reset! current-slide 0)
      "End" (reset! current-slide max-idx)
      nil)))

;; Add keyboard listener
(.addEventListener js/document "keydown" handle-keydown)

(defn keys-display
  []
  (let [slide (get slides @current-slide)]
    [:div.mt-6.max-w-md.mx-auto
     [:div.space-y-3
      (for [{:keys [key action]} (:keys slide)]
        ^{:key key}
        [:div.rounded-lg.p-4.text-center
         {:style {:background "rgba(20, 184, 166, 0.3)"}}
         [:div.text-3xl.font-bold.mb-2.text-white key]
         [:div.text-sm.text-white action]])]]))

(defn features-display
  []
  (let [slide (get slides @current-slide)]
    [:div.mt-6.max-w-2xl.mx-auto
     [:div.space-y-3
      (for [feature (:features slide)]
        ^{:key feature}
        [:div.rounded-lg.p-4.text-center
         {:style {:background "rgba(249, 115, 22, 0.3)"}}
         [:div.text-base.text-white feature]])]]))

(defn summary-display
  []
  (let [slide (get slides @current-slide)]
    [:div.mt-6.max-w-2xl.mx-auto
     [:div.space-y-3
      (for [point (:points slide)]
        ^{:key point}
        [:div.rounded-lg.p-4.text-center.text-base.text-white
         {:style {:background "rgba(236, 72, 153, 0.3)"}}
         point])]]))

(defn presentation
  []
  (let [{:keys [title subtitle content bg layout]} (get slides @current-slide)
        at-start? (zero? @current-slide)
        at-end? (= @current-slide (dec (count slides)))]
    [:div.flex.flex-col.h-full
     ;; Gradient content area
     [:div.flex-1.flex.flex-col.justify-center.p-6.rounded-lg
      {:style {:background bg}}
      [:div.text-center.text-white
       [:h1.text-4xl.font-bold.mb-3 title]
       (when subtitle
         [:p.text-xl.opacity-90.mb-6 subtitle])
       (when content
         [:p.text-lg.opacity-90.mb-4 content])
       ;; Layout-specific content
       (case layout
         :intro nil
         :keys [keys-display]
         :features [features-display]
         :summary [summary-display]
         nil)]]
     ;; Navigation centered below
     [:div.w-full.mt-4.pb-2
      [:div.text-center.mb-3
       [:div.text-base.font-semibold
        (str (inc @current-slide) " / " (count slides))]
       [:div.text-xs.text-gray-500.mt-1
        "Try arrow keys: ← →"]]
      [:div.flex.gap-4.mx-auto {:style {:width "fit-content"}}
       [:button.btn.btn-sm.btn-primary
        {:on-click #(swap! current-slide dec)
         :disabled at-start?
         :class (when at-start? "btn-disabled")}
        "← Previous"]
       [:button.btn.btn-sm.btn-primary
        {:on-click #(swap! current-slide inc)
         :disabled at-end?
         :class (when at-end? "btn-disabled")}
        "Next →"]]]]))

(rdom/render [presentation] (js/document.getElementById "keyboard-demo"))
(kind/hiccup
 [:div#keyboard-demo {:style {:min-height "550px"}}
  [:script {:type "application/x-scittle"
            :src "keyboard_nav.cljs"}]])

Thumbnail Overview

A professional touch for your presentations - let your audience see the big picture. Thumbnails provide visual context and make navigation instant.

Why thumbnails matter: - Quick overview of all slides - Jump to any section instantly - See where you are in the presentation - Makes long presentations more navigable

(ns scittle.presentations.thumbnails
  (:require
   [reagent.core :as r]
   [reagent.dom :as rdom]))

(defonce current-slide (r/atom 0))
(defonce show-thumbnails? (r/atom false))

(def slides
  [{:title "Thumbnail Overview"
    :content "Click 'Show Thumbnails' to see all slides at once"
    :bg "linear-gradient(to bottom right, #3b82f6, #9333ea)"}
   {:title "Quick Navigation"
    :content "Jump to any slide instantly"
    :bg "linear-gradient(to bottom right, #10b981, #14b8a6)"}
   {:title "Visual Context"
    :content "See where you are in the presentation"
    :bg "linear-gradient(to bottom right, #f59e0b, #f97316)"}
   {:title "Professional Touch"
    :content "Makes presentations feel polished"
    :bg "linear-gradient(to bottom right, #ef4444, #dc2626)"}
   {:title "Easy to Build"
    :content "Just render slides in a grid"
    :bg "linear-gradient(to bottom right, #8b5cf6, #9333ea)"}
   {:title "Scittle Powered"
    :content "No build tools needed!"
    :bg "linear-gradient(to bottom right, #ec4899, #f43f5e)"}])

(defn thumbnail-view []
  [:div.fixed.inset-0.bg-black.bg-opacity-75.z-50.overflow-auto
   {:on-click #(reset! show-thumbnails? false)}
   [:div.container.mx-auto.p-8
    [:div.grid.grid-cols-3.gap-4
     (for [[idx slide] (map-indexed vector slides)]
       ^{:key idx}
       [:div.cursor-pointer.transform.transition.hover:scale-105
        {:on-click (fn [e]
                     (.stopPropagation e)
                     (reset! current-slide idx)
                     (reset! show-thumbnails? false))}
        [:div.card.shadow-lg.rounded-lg
         {:class (when (= idx @current-slide) "ring-4 ring-blue-500")
          :style {:background (:bg slide)
                  :min-height "150px"}}
         [:div.card-body.text-white.text-center
          [:h3.text-lg.font-bold.mb-2 (:title slide)]
          [:p.text-sm.opacity-90 (:content slide)]
          [:div.text-xs.mt-2.opacity-75
           (str "Slide " (inc idx) "/" (count slides))]]]])]]])

(defn presentation
  []
  (let [{:keys [title content bg]} (get slides @current-slide)]
    [:div
     [:div.flex.flex-col.h-full
      ;; Gradient content area
      [:div.flex-1.flex.flex-col.justify-center.p-6.rounded-lg
       {:style {:background bg}}
       [:div.text-center.text-white
        [:h1.text-4xl.font-bold.mb-3 title]
        [:p.text-xl.opacity-90 content]]]
      ;; Navigation section - all centered
      [:div.mt-4
       ;; Show Thumbnails button - centered
       [:div.mb-4.text-center
        [:button.btn.btn-sm.btn-secondary
         {:on-click #(reset! show-thumbnails? true)}
         "📋 Show Thumbnails"]]
       ;; Centered page counter and navigation
       [:div
        [:div.text-center.mb-3
         [:span.text-base.font-semibold
          (str (inc @current-slide) " / " (count slides))]]
        [:div.flex.gap-2.mx-auto {:style {:width "fit-content"}}
         [:button.btn.btn-sm.btn-primary
          {:on-click #(swap! current-slide dec)
           :disabled (zero? @current-slide)
           :class (when (zero? @current-slide) "btn-disabled")}
          "←"]
         [:button.btn.btn-sm.btn-primary
          {:on-click #(swap! current-slide inc)
           :disabled (= @current-slide (dec (count slides)))
           :class (when (= @current-slide (dec (count slides))) "btn-disabled")}
          "→"]]]]]
     ;; Thumbnail overlay
     (when @show-thumbnails?
       [thumbnail-view])]))

(rdom/render [presentation] (js/document.getElementById "thumbnails-demo"))

Try the Thumbnail View

(kind/hiccup
 [:div#thumbnails-demo {:style {:min-height "550px"}}
  [:script {:type "application/x-scittle"
            :src "thumbnails.cljs"}]])

Live Code Editor

The killer feature: evaluating code directly in your presentation. Scittle provides scittle.core.eval_string - a function that evaluates ClojureScript code at runtime, in the browser.

Perfect for: - Interactive tutorials - Live coding demonstrations - Teaching ClojureScript - API documentation with runnable examples - Conference talks with audience participation

(ns scittle.presentations.code-editor
  (:require
   [reagent.core :as r]
   [reagent.dom :as rdom]))

(defonce code-input (r/atom "(+ 1 2 3)"))
(defonce output (r/atom ""))
(defonce error-msg (r/atom nil))

(defn eval-code!
  []
  (reset! error-msg nil)
  (try
    (let [result (js/scittle.core.eval_string @code-input)]
      (reset! output (pr-str result)))
    (catch js/Error e
      (reset! error-msg (.-message e))
      (reset! output ""))))

(defn code-editor
  []
  [:div.container.mx-auto.p-8
   [:div.card.shadow-lg
    [:div.card-body
     [:h2.text-2xl.font-bold.mb-4 "Live ClojureScript Editor"]
     [:p.text-gray-600.mb-4
      "Type ClojureScript code and click 'Evaluate' to see the result"]
     ;; Code input
     [:div.mb-4
      [:label.block.text-sm.font-semibold.mb-2 "Code:"]
      [:textarea.form-control.font-mono.text-sm
       {:value @code-input
        :on-change #(reset! code-input (-> % .-target .-value))
        :rows 6
        :placeholder "Enter ClojureScript code..."}]]
     ;; Evaluate button
     [:button.btn.btn-primary.mb-4
      {:on-click eval-code!}
      "▶ Evaluate"]
     ;; Output
     [:div
      [:label.block.text-sm.font-semibold.mb-2 "Result:"]
      (if @error-msg
        [:div.alert.alert-error
         [:strong "Error: "] @error-msg]
        [:pre.bg-gray-100.p-4.rounded.overflow-x-auto
         [:code.text-sm @output]])]

     ;; Examples
     [:div.mt-6
      [:p.text-sm.font-semibold.mb-2 "Try these examples:"]
      [:div.flex.flex-wrap.gap-2
       [:button.btn.btn-sm.btn-outline
        {:on-click #(reset! code-input "(+ 1 2 3 4 5)")}
        "Addition"]
       [:button.btn.btn-sm.btn-outline
        {:on-click #(reset! code-input "(map inc [1 2 3 4])")}
        "Map"]
       [:button.btn.btn-sm.btn-outline
        {:on-click #(reset! code-input "(filter even? (range 10))")}
        "Filter"]
       [:button.btn.btn-sm.btn-outline
        {:on-click #(reset! code-input "(reduce + (range 1 11))")}
        "Reduce"]
       [:button.btn.btn-sm.btn-outline
        {:on-click #(reset! code-input "(defn greet [name]\n  (str \"Hello, \" name \"!\"))\n\n(greet \"Scittle\")")}
        "Function"]]]]]
   [:div.card.shadow-lg.mt-6
    [:div.card-body
     [:h3.text-lg.font-bold.mb-2 "How It Works"]
     [:ul.list-disc.list-inside.space-y-2.text-gray-700
      [:li "Scittle provides " [:code.bg-gray-100.px-1 "scittle.core.eval_string"]]
      [:li "Evaluates ClojureScript code in the browser"]
      [:li "No compilation step - instant feedback"]
      [:li "Perfect for interactive documentation"]
      [:li "Great for teaching and demonstrations"]]]]])

(rdom/render [code-editor] (js/document.getElementById "code-editor-demo"))

Try It Yourself

Type any ClojureScript code and click “Evaluate” to see the result!

(kind/hiccup
 [:div#code-editor-demo {:style {:min-height "700px"}}
  [:script {:type "application/x-scittle"
            :src "code_editor.cljs"}]])

Shadow-cljs vs Scittle: Choosing the Right Tool

Both are excellent tools, but they serve different purposes. Choose based on your project’s needs:

(kind/hiccup
 [:div.overflow-x-auto
  [:table.table.table-zebra.w-full
   [:thead
    [:tr
     [:th "Feature"]
     [:th "Shadow-cljs"]
     [:th "Scittle"]]]
   [:tbody
    [:tr
     [:td [:strong "Compilation"]]
     [:td "Ahead-of-time (AOT)"]
     [:td "Just-in-time (JIT)"]]
    [:tr
     [:td [:strong "Setup Required"]]
     [:td "Yes - deps.edn, config"]
     [:td "No - just HTML + CDN"]]
    [:tr
     [:td [:strong "Build Time"]]
     [:td "Seconds to minutes"]
     [:td "Instant (no build)"]]
    [:tr
     [:td [:strong "Bundle Size"]]
     [:td "Optimized, tree-shaken"]
     [:td "~1MB runtime + code"]]
    [:tr
     [:td [:strong "Performance"]]
     [:td "Fastest (compiled)"]
     [:td "Fast (interpreted)"]]
    [:tr
     [:td [:strong "Hot Reload"]]
     [:td "Yes (via tooling)"]
     [:td "Just refresh browser"]]
    [:tr
     [:td [:strong "npm Integration"]]
     [:td "Full support"]
     [:td "Limited (CDN only)"]]
    [:tr
     [:td [:strong "Code Splitting"]]
     [:td "Yes"]
     [:td "Manual only"]]
    [:tr
     [:td [:strong "Best For"]]
     [:td "Production apps, SPAs"]
     [:td "Prototypes, demos, docs"]]
    [:tr
     [:td [:strong "Learning Curve"]]
     [:td "Moderate"]
     [:td "Very low"]]
    [:tr
     [:td [:strong "Deployment"]]
     [:td "Build + host static files"]
     [:td "Copy HTML file anywhere"]]]]])
Feature Shadow-cljs Scittle
Compilation Ahead-of-time (AOT) Just-in-time (JIT)
Setup Required Yes - deps.edn, config No - just HTML + CDN
Build Time Seconds to minutes Instant (no build)
Bundle Size Optimized, tree-shaken ~1MB runtime + code
Performance Fastest (compiled) Fast (interpreted)
Hot Reload Yes (via tooling) Just refresh browser
npm Integration Full support Limited (CDN only)
Code Splitting Yes Manual only
Best For Production apps, SPAs Prototypes, demos, docs
Learning Curve Moderate Very low
Deployment Build + host static files Copy HTML file anywhere

When to Use Scittle

Perfect for: - Technical presentations with live demos - Interactive documentation - Quick prototypes and experiments - Teaching ClojureScript to beginners - Shareable code examples (CodePen style) - Single-file applications

When to Use Shadow-cljs

Better for: - Production web applications - Large codebases with many dependencies - Performance-critical applications - Team projects requiring build tooling - Apps needing npm package integration - Progressive web apps (PWAs)

Best Practices & Tips

Structure Your Code

Keep your Scittle scripts organized and modular:

(kind/code
 ";; Good: Separate concerns
(ns my-app.core)

(defn render-slide [idx]
  ...)

(defn navigation-buttons []
  ...)

(defn main []
  (render-slide 0)
  (setup-keyboard-navigation!))")
;; Good: Separate concerns
(ns my-app.core)

(defn render-slide [idx]
  ...)

(defn navigation-buttons []
  ...)

(defn main []
  (render-slide 0)
  (setup-keyboard-navigation!))

Leverage CDN Libraries

Scittle has plugins for popular libraries: - Reagent - React wrapper (scittle.reagent) - Re-frame - State management (scittle.re-frame) - Promesa - Promises (scittle.promesa) - ClojureScript.test - Testing (scittle.cljs-test)

Use Browser DevTools

Scittle code is debuggable in Chrome/Firefox DevTools: - Set breakpoints in your ClojureScript code - Inspect atoms and state in the console - Use js/console.log for quick debugging - Check Network tab to ensure scripts load correctly

Performance Considerations

Keep it snappy: - Minimize initial script size (under 100KB recommended) - Use lazy loading for heavy computations - Cache expensive calculations in atoms - Consider memoization for pure functions - Test on slower devices/connections

Common Gotchas & Solutions

1. Namespace Loading Order

Problem: Trying to use a namespace before it’s loaded.

(kind/code
 ";; ❌ Won't work
(ns my-app.core
  (:require [my-app.utils :as utils]))

;; ✅ External file must load first
;; In HTML:
;; <script type=\"application/x-scittle\" src=\"utils.cljs\"></script>
;; <script type=\"application/x-scittle\" src=\"core.cljs\"></script>")
;; ❌ Won't work
(ns my-app.core
  (:require [my-app.utils :as utils]))

;; ✅ External file must load first
;; In HTML:
;; <script type="application/x-scittle" src="utils.cljs"></script>
;; <script type="application/x-scittle" src="core.cljs"></script>

2. Async Operations

Problem: Scittle scripts load asynchronously.

(kind/code
 ";; ❌ Might fail
(js/document.addEventListener \"DOMContentLoaded\"
  (fn [] (start-app!)))

;; ✅ Better: Use window load event
(js/window.addEventListener \"load\"
  (fn [] (start-app!)))")
;; ❌ Might fail
(js/document.addEventListener "DOMContentLoaded"
  (fn [] (start-app!)))

;; ✅ Better: Use window load event
(js/window.addEventListener "load"
  (fn [] (start-app!)))

3. CORS Issues

Problem: Loading scripts from different domains.

Solution: Either: - Host all files on the same domain - Use a CORS proxy for development - Configure server to allow CORS headers

4. Not All npm Packages Work

Scittle can’t use npm packages directly. Workarounds: - Use CDN versions (unpkg.com, jsdelivr.com) - Look for Scittle-compatible plugins - Use browser-native APIs instead

5. Error Messages

Scittle error messages can be cryptic. Tips: - Check browser console for stack traces - Use (js/console.log) liberally during development - Test small code snippets in isolation - Verify syntax with a Clojure linter

Resources & Next Steps

Official Documentation

Example Projects

  • Maria.cloud: Interactive ClojureScript learning platform
  • 4Clojure: Problem-solving platform using browser ClojureScript
  • Klipse: Interactive code snippets for blogs
  • This Presentation!: Available on GitHub

Community & Support

  • Clojurians Slack: #scittle and #clojurescript channels
  • ClojureVerse: Community forum for discussions
  • r/Clojure: Reddit community
  • Clojure Mailing List: Active community discussions

Try It Today!

Start building your first Scittle presentation:

(kind/code
 "<!DOCTYPE html>
<html>
<head>
  <script src=\"https://cdn.jsdelivr.net/npm/scittle@0.6.15/dist/scittle.js\"></script>
  <script src=\"https://cdn.jsdelivr.net/npm/scittle@0.6.15/dist/scittle.reagent.js\"></script>
</head>
<body>
  <div id=\"app\"></div>
  <script type=\"application/x-scittle\">
    (ns my-app.core
      (:require [reagent.core :as r]
                [reagent.dom :as rdom]))

    (defn app []
      [:div [:h1 \"Hello from Scittle!\"]])

    (rdom/render [app] (js/document.getElementById \"app\"))
  </script>
</body>
</html>")
<!DOCTYPE html>
<html>
<head>
  <script src="https://cdn.jsdelivr.net/npm/scittle@0.6.15/dist/scittle.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/scittle@0.6.15/dist/scittle.reagent.js"></script>
</head>
<body>
  <div id="app"></div>
  <script type="application/x-scittle">
    (ns my-app.core
      (:require [reagent.core :as r]
                [reagent.dom :as rdom]))

    (defn app []
      [:div [:h1 "Hello from Scittle!"]])

    (rdom/render [app] (js/document.getElementById "app"))
  </script>
</body>
</html>

Conclusion

Scittle brings the joy back to ClojureScript development.

No more waiting for builds. No more configuration headaches. Just open an HTML file and start coding.

Perfect for: - Learning ClojureScript - Creating interactive presentations - Building quick prototypes - Teaching and documentation

Remember: - Use Shadow-cljs for production applications - Use Scittle for everything else

Ready to build browser-native presentations? Fork this presentation and make it your own!


Thank you! Questions? Find me on Clojurians Slack.

Built with Scittle, Reagent, and ❤️ for functional programming

source: src/scittle/presentations/browser_native_slides.clj