README.md

April 19, 2026 ยท View on GitHub

๐Ÿ–ฅ๏ธ Page Loader

A simple Scala.js webpage loader for Scala web servers.

Build status Maven Central Documentation



"There is nothing new under the sun." โ€” Ecclesiastes.


โœ”๏ธ Features

This tool is extremely small and minimalistic, with absolutely no bells or whistles.

  1. Generates a shared HTML shell template that bootstraps any Scala.js view.
  2. Selects the correct view at request time by injecting its exported name into the template.
  3. Supports automatic hot reload during development via a WebSocket heartbeat.

โฌ‡๏ธ Installation

Choose one server integration and one client integration from the sections below โ€” that's all you need.

Compiled with Scala 3.8.3, with no intention to explicitly support older versions.

โš™๏ธ Example

Server

Define a ViewData for each page, then wire it up to a Tapir endpoint using showView:

import com.alecdorrington.pageloader.ViewData
import com.alecdorrington.pageloader.tapir.{HeartbeatService, ViewEndpoint}

val indexView = ViewData(name = "IndexView", pageTitle = "Home")
val aboutView = ViewData(name = "AboutView", pageTitle = "About")

val heartbeat = HeartbeatService[IO]()

val endpoints = List(
  endpoint.get.in("").showView(indexView),
  endpoint.get.in("about").showView(aboutView),
) ++ heartbeat.api

Client

Each view is a Scala.js object exported with @JSExportTopLevel. The name passed to the annotation must match the name field of the corresponding ViewData on the server.

import com.raquo.laminar.api.L.{*, given}
import com.alecdorrington.pageloader.laminar.LaminarView
import scala.scalajs.js.annotation.JSExportTopLevel

@JSExportTopLevel("IndexView")
object IndexView extends LaminarView:

  override def content = div(
    h1("Welcome to my website!"),
    p("We're still getting set up here... Stay tuned!"),
  )

๐Ÿ“ก Server Integration

Currently, a connector exists for only a single web framework: Tapir. In principle, any future connectors will be published as separate dependencies with the name page-loader-{web-framework}. Contributions are welcome!

Tapir

Tapir is a library to describe HTTP APIs and expose them as a server. A separate connector is provided to easily attach a ViewData to a Tapir endpoint. Add the following dependency:

libraryDependencies += "com.alecdorrington" %% "page-loader-tapir" % "0.2.2"

Use the showView extension method to convert any Tapir GET endpoint into one that serves a page:

endpoint.get.in("").showView(ViewData(name = "IndexView", pageTitle = "Home"))

Hot Reload

HeartbeatService exposes a WebSocket endpoint that the client connects to. When the server restarts, the disconnection is detected and the page reloads automatically. This is enabled by default when using showView.

val heartbeat = HeartbeatService[IO]()
// Add heartbeat.api to your server alongside your page endpoints.

To disable hot reload for a specific page, pass hotReloadWebsocketPath = None:

endpoint.get.in("").showView(view, hotReloadWebsocketPath = None)

๐ŸŽจ Client Integration

Currently, a connector exists for only a single UI framework: Laminar. In principle, any future connectors will be published as separate dependencies with the name page-loader-{ui-framework}. Contributions are welcome!

Laminar

Laminar is a reactive UI library for Scala.js. A separate connector provides LaminarView, a base trait that handles rendering automatically. Add the following dependency:

libraryDependencies += "com.alecdorrington" %%% "page-loader-laminar" % "0.2.2"

Extend LaminarView and implement content:

import com.raquo.laminar.api.L.{*, given}
import com.alecdorrington.pageloader.laminar.LaminarView
import scala.scalajs.js.annotation.JSExportTopLevel

@JSExportTopLevel("IndexView")
object IndexView extends LaminarView:

  override def content = div(
    h1("Welcome to my website!"),
    p("We're still getting set up here... Stay tuned!"),
  )

๐Ÿ‘๏ธ See also