a reactive (signals based) hypermedia web framework (wip) stormlightlabs.github.io/volt/
hypermedia frontend signals
1# Lifecycle Hooks 2 3Volt's runtime exposes lifecycle hooks so you can observe mounts, run cleanup logic, and coordinate plugins without re-implementing binding internals. Hooks run consistently for both SSR hydration and client-only mounts. 4 5## Lifecycle Layers 6 7- **Global hooks** fire for every mount/unmount operation and are ideal for analytics, logging, or cross-cutting concerns. 8- **Element hooks** attach to a single DOM element and let you react to that element entering or leaving the document. 9- **Plugin hooks** are available while authoring custom bindings and let you scope mount/unmount work to a plugin instance. 10 11## Global Hooks 12 13Register global hooks with `registerGlobalHook(name, callback)`. The available events are: 14 15| Event | Position | 16| -------------------------- | ---------------------------------------------------------------------------------------------------- | 17| `beforeMount(root, scope)` | Runs right before bindings initialize | 18| | This is the place to patch the scope or read serialized state | 19| `afterMount(root, scope)` | Runs after VoltX has attached bindings and lifecycle state | 20| `beforeUnmount(root)` | Runs before a root is torn down, giving you time to flush pending work | 21| `afterUnmount(root)` | Runs after cleanup finishes | 22| | Use this to release global resources | 23 24```ts 25import { registerGlobalHook } from "@volt/volt"; 26 27const unregister = registerGlobalHook("afterMount", (root, scope) => { 28 console.debug("[volt] mounted", root.id, scope); 29}); 30 31unregister(); 32``` 33 34### Working with the Scope Object 35 36`beforeMount` and `afterMount` receive the reactive scope for the root element so you can read signal values or stash helpers on the scope. 37Avoid mutating DOM inside these hooks-leave DOM updates to bindings/plugins to prevent hydration mismatches. 38 39### Managing Global Hooks 40 41- Use `unregisterGlobalHook` when the callback is no longer needed. 42- Call `clearGlobalHooks("beforeMount")` or `clearAllGlobalHooks()` in test teardown code to avoid cross-test leakage. 43- Prefer one central module to register global hooks so they are easy to audit. 44 45## Element Hooks 46 47When you need per-element notifications, register element hooks: 48 49```ts 50import { registerElementHook, isElementMounted } from "@volt/volt"; 51 52const panel = document.querySelector("[data-volt-panel]"); 53 54registerElementHook(panel!, "mount", () => { 55 console.log("panel is live"); 56}); 57 58registerElementHook(panel!, "unmount", () => { 59 console.log("panel removed, dispose timers"); 60}); 61 62if (isElementMounted(panel!)) { 63 // Safe to touch DOM or read bindings immediately. 64} 65``` 66 67Element hooks automatically dispose after the element unmounts. Use `getElementBindings(element)` when debugging to see which binding directives are attached to a node. 68 69## Plugin Lifecycle Hooks 70 71Custom plugins receive lifecycle helpers on the plugin context: 72 73```ts 74import type { PluginContext } from "@volt/volt"; 75 76export function focusPlugin(ctx: PluginContext) { 77 const el = ctx.element as HTMLElement; 78 79 ctx.lifecycle.onMount(() => el.focus()); 80 ctx.lifecycle.onUnmount(() => el.blur()); 81} 82``` 83 84- `ctx.lifecycle.onMount` and `ctx.lifecycle.onUnmount` let you coordinate DOM state with the binding's lifetime. 85- Use `ctx.lifecycle.beforeBinding` and `ctx.lifecycle.afterBinding` to measure binding creation or guard against duplicate initialization. 86- Always combine lifecycle hooks with `ctx.addCleanup` if you create subscriptions that outlive a single mount cycle. 87 88## Best Practices 89 90- Keep hook callbacks side-effect free whenever possible; defer heavy work to asynchronous tasks. 91- Never mutate the DOM tree that VoltX currently manages from `beforeMount`; wait for `afterMount` or plugin hooks instead. 92- When adding analytics or telemetry, remember to remove hooks on navigation or single-page route changes to avoid duplicate events. 93- In tests, seed hooks inside the test body and tear them down with the disposer returned from `registerGlobalHook` to preserve isolation. 94 95For server-rendered workflows and hydration patterns, refer to [ssr](./ssr).