Skip to content
scalive
Menu
ConnectingLiveReconnectingOffline
GitHub

Typed counter

A LiveView with a count model and Decrement, Reset, and Increment messages.

LiveView
scala
final class CounterExample extends LiveView[CounterExample.Msg, CounterExample.Model]:
  import CounterExample.*

  def mount(ctx: MountContext): Task[Model] =
    ZIO.succeed(Model(count = 0))

  def handleMessage(model: Model, ctx: MessageContext) =
    case Msg.Decrement => ZIO.succeed(model.copy(count = model.count - 1))
    case Msg.Increment => ZIO.succeed(model.copy(count = model.count + 1))
    case Msg.Reset     => ZIO.succeed(model.copy(count = 0))

  def view(model: Signal[Model]): HtmlElement[Msg] =
    div(
      cls := "docs-counter",
      p(
        cls         := "docs-counter-status",
        role        := "status",
        aria.live   := "polite",
        aria.atomic := true,
        span(
          cls := "docs-counter-model-label",
          span("Current model"),
          code("count =")
        ),
        strong(cls := "docs-counter-value", model.map(_.count.toString))
      ),
      fieldSet(
        dataAttr("example-controls") := "",
        legend(cls := "docs-visually-hidden", "Counter controls"),
        button(
          typ := "button",
          cls := "docs-counter-secondary",
          on.click(Msg.Decrement),
          "Decrease"
        ),
        button(
          typ := "button",
          cls := "docs-counter-secondary",
          on.click(Msg.Reset),
          "Reset"
        ),
        button(
          typ := "button",
          cls := "docs-counter-increment",
          on.click(Msg.Increment),
          "Increase"
        )
      )
    )
end CounterExample

object CounterExample:
  enum Msg:
    case Decrement, Increment, Reset

  final case class Model(count: Int)

Live result

ConnectedReconnectingRead-only

Current modelcount =0

Counter controls

Disconnected. Controls resume after reconnection.

LiveView observability

Interaction inspector

See how actions in the live result above travel through typed messages, server state, protocol frames, and DOM updates.

Reconnect to inspect live interactions

The live result and its inspector resume when the connection returns.

Related guidance: start with models, typed messages, and rendering.