Lifecycle Hooks
Mount, update, unmount, error boundaries, and browser window events — all auto-cleaned on unmount.
Components register lifecycle handlers in their init block via the lifecycle DSL.
Each hook can be registered multiple times; all handlers fire in registration order.
onMount for
analytics, another for initializing a third-party widget) without having to merge them into a single handler.
init {
lifecycle {
onMount {
// First handler — runs first
analytics.trackView("dashboard")
}
onMount {
// Second handler — runs after the first
tooltip = Tooltip(dom!!)
}
}
} class MyComponent(ctx: Ctx<Props>) : Component<Props>(ctx) {
init {
lifecycle {
onMount { /* ... */ }
onUpdate { /* ... */ }
onUnmount { /* ... */ }
onNextProps { new, old -> /* ... */ }
onError { e -> /* ... */ }
onResize { entries -> /* ... */ }
onWindowResize { v -> /* ... */ }
onWindowFocus { /* ... */ }
onWindowBlur { /* ... */ }
}
}
} subscribingTo()) auto-unsubscribe when the component unmounts. Window
and resize hooks registered through the lifecycle DSL also clean themselves up. You rarely need to track
unsubscribe functions manually.
Core hooks
onMount
Fires after the component's DOM has been rendered. The dom property now points at the actual
HTMLElement.
init {
lifecycle {
onMount {
// dom is available here
console.log("Mounted at", dom?.tagName)
// Attach third-party widgets that need a live element
val tooltip = Tooltip(dom!!)
}
}
} onUpdate
Fires after every re-render (state change, props change, parent redraw). Use this when you need to run code after the DOM has been updated — for example, scrolling a container into view or syncing a third-party widget.
init {
lifecycle {
onUpdate {
// Component re-rendered; DOM is in sync with state
console.log("child count", dom?.children?.length)
}
}
} onUnmount
Fires when the component is removed from the DOM. Clean up anything the lifecycle doesn't handle for you (third-party widgets, manually-registered listeners).
private var tooltip: Tooltip? = null
init {
lifecycle {
onMount { tooltip = Tooltip(dom!!) }
onUnmount { tooltip?.destroy(); tooltip = null }
}
} onNextProps
Fires when a parent passes new props. Receives both the new and old props, so you can react to specific changes.
init {
lifecycle {
onNextProps { new, old ->
if (new.query != old.query) {
reload()
}
}
}
} onError — error boundaries
Errors thrown during render or by child components bubble up the component tree, looking for an
onError
handler. The nearest ancestor with an onError catches the error; if none do, the error propagates
to
Preact's default handling.
class Dashboard(ctx: NoProps) : PureComponent(ctx) {
private var error: Throwable? by value(null)
init {
lifecycle {
onError { e ->
// Log, report to analytics, and show a fallback
console.error("Caught", e.message)
error = e
}
}
}
override fun VDom.render() {
val err = error
if (err != null) {
ui.negative.message {
+"Something went wrong: ${err.message}"
ui.button {
onClick { error = null }
+"Retry"
}
}
return
}
// Any error thrown inside these children bubbles up to onError above
WidgetGrid()
SalesChart()
ActivityFeed()
}
} onError hook. If you want errors to bubble past a
component that already has a handler, don't catch them there — handle them higher up.
onResize — element resize (ResizeObserver)
Wraps the browser's ResizeObserver API and observes this component's DOM element.
The observer is attached on mount and disconnected on unmount automatically.
class ResponsiveCanvas(ctx: NoProps) : PureComponent(ctx) {
private var width: Double by value(0.0)
private var height: Double by value(0.0)
init {
lifecycle {
onResize { entries ->
val box = entries[0].contentRect
width = box.width
height = box.height
}
}
}
override fun VDom.render() {
div {
css {
this.width = 100.pct
this.height = 100.pct
}
+"${width.toInt()} x ${height.toInt()}"
}
}
} Useful for canvas games, responsive charts, or anything that needs to adapt when its container changes size — fires even when the element is resized by a sibling layout change, not just window resize.
onWindowResize / onWindowFocus / onWindowBlur — browser window events
For events on the browser window, use these hooks. They dispatch through the app-level
WindowController (registered by default in every kraftApp ) and auto-unsubscribe
on unmount.
class WindowTracker(ctx: NoProps) : PureComponent(ctx) {
private var size: Vector2D? by value(null)
private var focused: Boolean by value(true)
init {
lifecycle {
onWindowResize { v: Vector2D ->
// v.x = window.innerWidth, v.y = window.innerHeight
size = v
}
onWindowFocus {
focused = true
}
onWindowBlur {
focused = false
}
}
}
override fun VDom.render() {
div {
+"window: ${size?.x?.toInt()} x ${size?.y?.toInt()}"
+" | focused: $focused"
}
}
} onResize for layout-aware components that need to react
to
their own element changing size. Use onWindowResize for global concerns like
viewport-based
layout or responsive breakpoints.
Reference — all hooks
| Hook | Signature | Fires when |
|---|---|---|
onMount | () -> Unit | Component inserted into the DOM |
onUpdate | () -> Unit | Component re-rendered (DOM updated) |
onUnmount | () -> Unit | Component removed from the DOM |
onNextProps | (new, old) -> Unit | Parent passes new props |
onError | (Throwable) -> Unit | Error thrown in render or child component |
onResize | (Array<ResizeObserverEntry>) -> Unit | Component's DOM element resized |
onWindowResize | (Vector2D) -> Unit | Browser window resized |
onWindowFocus | () -> Unit | Browser window gains focus |
onWindowBlur | () -> Unit | Browser window loses focus |