Skip to content
Brew

Brew lifecycle

Brew runs a fixed sequence of lifecycle hooks on mount and on every update. Hooks are ordinary methods found by name—they are not callable from the browser.

Order on the first render:

  1. Assign matching mount/params onto properties
  2. boot()
  3. Interface-mixin boot… / initialize…
  4. mount(...) (and mixin mount…)
  5. booted()
  6. Render (rendering → HTML → rendered)

mount receives tag attributes / Brew.mount params (and can take container-resolved trailing args).

class Counter : Component() {
var count = 0
fun mount(start: Int = 0) {
count = start
}
fun boot() {
// runs on mount and on every subsequent request
}
}

On POST /brew/update, Brew restores state from the snapshot, then:

  1. boot / mixin boot / initialize…
  2. hydrate / hydrateFoo
  3. booted
  4. Property updates → updating / updatingFoo / nested variants, then updated…
  5. Action calls
  6. dehydrate / dehydrateFoo
  7. Render (unless skipped)
Hook When
updating(path, value) Before any property write
updated(path, value) After any property write
updatingFoo / updatedFoo For property foo
Nested updatedFoo($value, $key) style for foo.bar paths
fun updatedCount(value: Int) {
// count just changed
}
fun updating(path: String, value: Any?) {
// any path, e.g. "todos.0.title"
}
fun rendering(view: BrewView, data: MutableMap<String, Any?>) {
data["extra"] = true
}
fun rendered(view: BrewView, html: String) {
// inspect or log final HTML
}
fun exception(e: Throwable, stopPropagation: () -> Unit) {
if (e is IllegalStateException) stopPropagation()
}

When your component implements interfaces, Brew also looks for mixin-style hooks such as bootWithX, mountWithX, hydrateWithX, updatingWithX (interface simple name appended). Use them to share behaviour across component families without inheritance.

fun touch() {
skipRender() // or @Renderless on the method
}
  • Properties — what hooks observe
  • Actions — what runs between hydrate and dehydrate
  • Testing — assert lifecycle outcomes