Skip to content
scalive
Menu
ConnectingLiveReconnectingOffline
GitHub

Lifecycle and connection state

Compare the connected mount with declarative LiveSocket state, put and clear one keyed notification, and change the title projected from the model. The example runs as an isolated nested LiveView, so it displays its projected title without claiming ownership of the documentation page's browser title.

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

  override def hooks: LiveHooks[Msg, Model] =
    LiveHooks.empty[Msg, Model].afterRender { (model, ctx) =>
      ZIO
        .when(ctx.connection.isInstanceOf[Connection.Connected[?]]) {
          ZIO.logDebug(s"Lifecycle example rendered with title '${model.currentTitle}'")
        }.unit
    }

  override def pageTitle(model: Model): Option[String] = Some(model.currentTitle)

  def mount(ctx: MountContext): Task[Model] =
    ZIO.succeed(
      Model(
        connectedMount = ctx.connection.isInstanceOf[Connection.Connected[?]],
        currentTitle = DefaultTitle
      )
    )

  def handleMessage(model: Model, ctx: MessageContext) =
    case Msg.PutNotification =>
      ctx.flash.put(NotificationFlash, "Your notification is ready.").as(model)
    case Msg.ClearNotification =>
      ctx.flash.clear(NotificationFlash).as(model)
    case Msg.RequestAttention =>
      ZIO.succeed(model.copy(currentTitle = AttentionTitle))
    case Msg.Reset =>
      ctx.flash.clear(NotificationFlash).as(model.copy(currentTitle = DefaultTitle))

  def view(model: Signal[Model]): HtmlElement[Msg] =
    div(
      cls := "docs-lifecycle",
      div(
        cls := "docs-lifecycle-state",
        sectionTag(
          p(cls := "docs-lifecycle-label", "Mount phase"),
          strong(
            dataAttr("mount-phase") := "",
            model.map(model =>
              if model.connectedMount then "Connected socket mount" else "Disconnected HTTP mount"
            )
          )
        ),
        sectionTag(
          p(cls                              := "docs-lifecycle-label", "Projected page title"),
          strong(dataAttr("lifecycle-title") := "", model.map(_.currentTitle))
        )
      ),
      div(
        cls := "docs-lifecycle-connection",
        span(connection.visibleWhenConnected, "LiveSocket connected"),
        span(connection.visibleWhenDisconnected, "LiveSocket disconnected")
      ),
      flash(NotificationFlash) { message =>
        p(
          dataAttr("lifecycle-flash") := "",
          cls                         := "docs-lifecycle-flash",
          role                        := "status",
          message
        )
      },
      fieldSet(
        dataAttr("example-controls") := "",
        legend(cls := "docs-visually-hidden", "Lifecycle controls"),
        button(typ := "button", on.click(Msg.PutNotification), "Show notification"),
        button(typ := "button", on.click(Msg.ClearNotification), "Clear notification"),
        button(typ := "button", on.click(Msg.RequestAttention), "Request attention"),
        button(typ := "button", on.click(Msg.Reset), "Reset example")
      )
    )
end LifecycleExample

object LifecycleExample:
  final case class Model(connectedMount: Boolean, currentTitle: String)

  enum Msg:
    case PutNotification
    case ClearNotification
    case RequestAttention
    case Reset

  private val NotificationFlash = FlashKind("notification")
  private val DefaultTitle      = "Lifecycle example"
  private val AttentionTitle    = "Attention needed"

Live result

ConnectedReconnectingRead-only

Mount phase

Disconnected HTTP mount

Projected page title

Lifecycle example
LiveSocket disconnected
Lifecycle 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: follow the complete lifecycle or apply flash, title, and connection UX.