Skip to content

Output modes

kslides can render to two destinations, independently or together. Both are configured inside output { }.

Static site

fun staticOutput() {
  kslides {
    output {
      enableFileSystem = true
      enableHttp = false
      // outputDir defaults to "docs"
    }

    presentation {
      path = "index.html"
      markdownSlide { content { "# Static site" } }
    }
  }
}

Files land under outputDir (default docs/) β€” perfect for pushing to GitHub Pages or Netlify.

When the deck uses playground { }, letsPlot { }, or diagram { }, kslides emits the iframe content as separate HTML files under docs/playground/, docs/letsPlot/, and docs/kroki/ respectively.

HTTP server

fun httpOutput() {
  kslides {
    output {
      enableFileSystem = false
      enableHttp = true
      httpPort = 8080
    }

    presentation {
      path = "index.html"
      markdownSlide { content { "# Live server" } }
    }
  }
}

This starts a Ktor server on the chosen port. Iframe content is generated on the fly and cached per session.

Dev mode (live reload)

fun devModeOutput() {
  kslides {
    output {
      // Live-reload dev server: the page refreshes to the current slide when the app restarts.
      enableHttp = true
      devMode = true
      enableFileSystem = false // no need to write static files while authoring
    }

    presentation {
      path = "index.html"
      markdownSlide { content { "# Editing live" } }
    }
  }
}

With devMode = true (which requires enableHttp), every served page embeds a small client that reconnects to the server over a websocket and refreshes the browser β€” restoring the current slide and fragment β€” whenever the server restarts.

Because slide content is compiled Kotlin, picking up an edit requires restarting the JVM. Trigger that however you like:

  • ./kslides-dev.sh β€” a watcher script that recompiles and restarts the app on every source change, so the loop is fully automatic: edit a slide, save, and the browser updates on the same slide.
  • IDE rerun β€” press Run again on main(); the browser reconnects and refreshes. This is also the path on Windows.

./gradlew -t run is not a reliable trigger: Gradle's continuous build cannot restart a long-running (blocking) server task.

Note: keep devMode out of published builds. If enableFileSystem is also enabled, the generated static pages under outputDir embed the reload client too. That is harmless on a static host β€” the client simply can't reach the websocket and retries quietly β€” but it is dead weight in your deployable output. Use devMode = false (or disable filesystem output) for the run that produces the docs/ you publish.

Multiple presentations in one program

fun multiplePresentations() {
  kslides {
    presentation {
      path = "index.html"
      markdownSlide { content { "# Welcome" } }
    }

    presentation {
      path = "talks/2026.html"
      markdownSlide { content { "# 2026 talk" } }
    }
  }
}

Each presentation { } becomes a separate page; nested directories under path map to nested directories on disk.

When to use which

You want… Use
Deploy to GitHub Pages / Netlify enableFileSystem = true
Local preview during development enableHttp = true (default)
Render dynamic data per request HTTP only
Both β€” preview locally, deploy the static artifact Leave both enabled