Skip to content
Domphy

Lifecycle

Hooks fire in a fixed linear sequence. Each step exposes more of the node as it progresses.

Lifecycle Hooks
import { DomphyElement, merge } from "@domphy/core"

const App: DomphyElement<"div"> = {
  div: "Hello",

  _onSchedule: (node, rawElement) => {
    const theme = node.getContext("theme")
    merge(rawElement, {
      style: {
        color: theme === "dark" ? "#fff" : "#000",
      },
    })
  },

  _onInsert: (node) => {
    const index = node.parent!.children.items.indexOf(node)
    node.setMetadata("index", index)
  },

  _onMount: (node) => {
    const observer = new ResizeObserver(() => {
      console.log(node.domElement!.offsetWidth)
    })

    observer.observe(node.domElement!)
    node.addHook("BeforeRemove", () => observer.disconnect())
  },

  _onBeforeUpdate: (node, rawChildren) => {
    console.log("incoming:", rawChildren.length)
  },

  _onUpdate: (node) => {
    console.log("children updated:", node.children.items.length)
  },

  _onBeforeRemove: (node, done) => {
    node.domElement!
      .animate([{ opacity: 1 }, { opacity: 0 }], { duration: 300 })
      .onfinish = done
  },

  _onRemove: (node) => {
    console.log("removed")
  },
}

Hook Order

HookWhenWhat's available
_onSchedule(node, raw)Before parsing, while the raw element can still be changedparent, _context, _metadata, mutable raw
_onInit(node)After parsing, before insertionnode properties, no siblings yet
_onInsert(node)Added to the parent child listsiblings, position in tree
_onMount(node)DOM element created and connected to the nodedomElement and all node properties
_onBeforeUpdate(node, rawChildren)Before a child update cycle applies new childrencurrent node, current DOM, incoming raw children
_onUpdate(node)After the update cycle finishesupdated children and domElement
_onBeforeRemove(node, done)Before removal, must call done()domElement, current runtime state
_onRemove(node)After the node is fully removednode instance after removal work completes
_onError(node, error, reset)An error was thrown by a reactive child expression in this subtreeAll node properties; call reset() to clear children and render fallback UI. If no ancestor handles it, the error is logged to console.

For a declarative error boundary, use the errorBoundary() patch from @domphy/ui, which wraps _onError with a fallback-rendering API — see Error Boundary.

_onSchedule is the right place to apply context-aware patches. Unlike inline $: [patches], it can read parent context before parsing begins.

Hooks fire once per real DOM node, not once per patch-factory call. A patch factory invoked again by a reactive ancestor (the node is reused, not recreated) gets a brand-new closure, but _onInit/_onMount/etc. do NOT re-run on that reused node — so imperative state wired inside them (a document listener, a ResizeObserver) stays bound to whichever generation attached it first. For per-node imperative state that must track the CURRENT generation's props across re-renders, use behavior(key, attach, props) instead of a raw _onMount — see Common Patterns → Per-node behavior.

See also ElementNode API.