Flash messages, page titles, and connection status
Before you start
You need a LiveView with rendered state. The title section also requires a
root layout that renders liveTitle. Review Lifecycle and connection behavior
before attaching effects to lifecycle hooks.
Render keyed flash
Define a stable FlashKindopaque type FlashKind = StringA nominal key for one category of flash message., update it through the phase context, and render only
that key with flashobject flashRenders flash messages set with ctx.flash.. Saved is
FlashKind("saved"). Put the message from handleMessage:
case Msg.Save =>
documents.save(model.title) *> ctx.flash.put(Saved, "Changes saved.").as(model)View source (documentation/fixtures/guides/src/feedback/DocumentLiveView.scala:40-41)
Then render that key in view:
flash(Saved) { message =>
p(role := "status", aria.live := "polite", message)
},View source (documentation/fixtures/guides/src/feedback/DocumentLiveView.scala:47-49)
putdef put(kind: FlashKind, message: String): zio.package.Task[Unit]Sets the message for kind, replacing any previous one. replaces the value for the same key.
cleardef clear(kind: FlashKind): zio.package.Task[Unit]Removes the message for kind. removes that key, and
clearAlldef clearAll: zio.package.Task[Unit]Removes every message. removes every flash value owned by the current lifecycle. Prefer a
specific key when independent notices can coexist.
Flash is lifecycle state, not a substitute for model data. Use it for brief feedback such as a save result or navigation notice. Keep values free of secrets, and put information that must survive arbitrary reloads in durable application state instead.
Derive the page title
Override pageTitledef pageTitle(model: Model): Option[String]Returns the document title for model, or None to use the root layout's default. on the routed root
LiveViewtrait LiveView[Msg, Model]Defines a server-rendered page with typed messages and model state. and derive it from the same
model exposed as a signal to viewdef view(model: Signal[Model]): HtmlElement[Msg]Describes the HTML for every model this lifecycle will have.:
override def pageTitle(model: Model): Option[String] =
Some(model.title)View source (documentation/fixtures/guides/src/feedback/DocumentLiveView.scala:31-32)
The root layout's liveTitledef liveTitle(pageTitle: Option[String], default: String, prefix: String = ..., suffix: String = ...): HtmlElement[Nothing]Renders the document <title> in a root layout, so the LiveView's pageTitle can change it. renders that title during disconnected HTTP
rendering. Connected model changes send title metadata so the client updates
document.title. Returning None or a blank title uses the root layout's
fallback.
Only the root LiveViewtrait LiveView[Msg, Model]Defines a server-rendered page with typed messages and model state. owns the document title. A nested
LiveViewtrait LiveView[Msg, Model]Defines a server-rendered page with typed messages and model state. can project a
title-like value for its own interface, but its pageTitledef pageTitle(model: Model): Option[String]Returns the document title for model, or None to use the root layout's default. result does not
replace the containing document's title. The embedded lifecycle example makes
that boundary visible by displaying its projection inside the example.
Show connection state
Render both states and let declarative connection bindings switch them in the browser:
div(
span(connection.visibleWhenConnected, "Connected"),
span(connection.visibleWhenDisconnected, "Offline")
),View source (documentation/fixtures/guides/src/feedback/DocumentLiveView.scala:52-55)
The disconnected state is visible in static HTML by default. Once the LiveSocket connects, the client hides it and reveals the connected state; it reverses those attributes if the transport drops. Include text or another non-color cue so the state remains understandable without color perception.
Disable controls while offline
A control that needs the server should not look usable while the page is
offline. Render it disabled, then let the connection bindings run
JSval JS: JSCommands.JSCommand[Nothing]The empty JSCommand from which client commands are composed. commands that enable it once the page
connects and disable it again if the connection drops:
button(
typ := "button",
disabled := true,
connection.onConnect(JS.removeAttribute("disabled")),
connection.onDisconnect(JS.setAttribute("disabled" -> "")),
on.click(Msg.Save),
"Save"
)View source (documentation/fixtures/guides/src/feedback/DocumentLiveView.scala:59-66)
The button stays disabled in the static HTML and until the first connection,
so an early click cannot be lost. The client applies the bindings again after
each reconnect, and attributes changed by JS commands survive later server
patches.
Put connection bindings in the root LiveView. When the connection drops, the client tears down nested LiveViews before it runs the disconnected commands, so bindings inside a nested LiveView do not react to the outage.
Keep after-render effects observational
Declare static hooks once on the LiveViewtrait LiveView[Msg, Model]Defines a server-rendered page with typed messages and model state.:
override def hooks: LiveHooks[Msg, Model] =
LiveHooks.afterRender { (model, ctx) =>
ctx.connection match
case Connection.Connected(capabilities) =>
capabilities.client.push(RenderedEvent, Rendered(model.title))
case Connection.Disconnected => ZIO.unit
}View source (documentation/fixtures/guides/src/feedback/DocumentLiveView.scala:21-27)
An after-render hook observes a render that already succeeded and cannot
change the model. When an effect must produce the next state, use
handleMessagedef handleMessage(model: Model, ctx: MessageContext): Msg => zio.package.Task[Model]Handles a message against the current immutable model.,
handleParamsdef handleParams(model: Model, params: Params, url: zio.http.URL, ctx: ParamsContext): zio.package.Task[Model]Updates the model for parameters decoded from the current URL., an async
completion, or a subscription message instead.
Lifecycle hooks describes the rest of
the after-render contract.
Exercise the behavior
Use the lifecycle example to put and clear flash,
change the projected title, inspect connected mount state, and reset the nested
LiveViewtrait LiveView[Msg, Model]Defines a server-rendered page with typed messages and model state.. Its source keeps the model, messages, flash key, title projection, and
after-render hook together.
For the full lifecycle sequence and reconnect model, read Lifecycle, state ownership, and reconnects.
Related tasks
Install the title-owning document shell with Layouts.
Deliver delayed UI state through Asynchronous work.
Verify title and reconnect behavior with Testing LiveViews.