Errors in Lifecycle Hooks
geajs has no equivalent of React's error boundaries. Nothing in the framework catches an exception thrown from created() or onAfterRender() and swaps in a fallback UI. What actually happens is worse than "that one component fails to render" — it's worth understanding precisely, because the failure mode is more severe than it looks at first glance.
What Happens When created() Throws
Consider a tree with two unrelated components mounted side by side:
export default class App extends Component {
template() {
return (
<div>
<Problematic />
<UnrelatedSibling />
</div>
)
}
}
If Problematic.created() throws, the exception surfaces as an uncaught error in the console — and the entire initial render aborts. UnrelatedSibling never mounts either, even though it has nothing to do with Problematic and would have rendered fine on its own. The result is a blank page: document.getElementById('app') ends up with no children at all, not even the parts of the tree that had no problems.
This is because component construction across the tree happens as a single synchronous unit before anything is inserted into the DOM. One exception anywhere in that unit aborts the whole unit — there's no per-component isolation.
onAfterRender() Behaves the Same Way
Despite what the name might suggest, onAfterRender() does not run in some later, isolated pass after the DOM is settled — it runs synchronously, inline, as part of the same render() call that constructs the tree. A throw here produces the identical failure mode as a throw in created(): the whole tree fails to mount, unrelated siblings included.
onAfterRenderAsync() Is Currently Broken (Unrelated to Error Handling)
Worth flagging separately: at the time of writing, onAfterRenderAsync() cannot be used at all with the current @geajs/core / @geajs/vite-plugin version combination — the build fails with a SyntaxError before the component can even render, due to a missing export in the plugin's generated runtime module. This isn't a consequence of anything covered on this page; it's a packaging bug, already filed upstream. Until it's fixed, treat onAfterRenderAsync() as unavailable rather than as a hook with its own error-handling story to plan around.
Writing Defensively, Given No Boundary Exists
Since nothing rescues you from a thrown exception in created() or onAfterRender(), the responsibility falls entirely on the code you write inside them. A few concrete habits:
Wrap risky synchronous work in try/catch yourself. If created() does anything that can realistically fail — parsing a prop, reading from localStorage, calling into a third-party library — catch it there and fall back to a safe default rather than letting it propagate:
export default class Chart extends Component {
parsedConfig: ChartConfig | null = null
configError: string | null = null
created() {
try {
this.parsedConfig = parseChartConfig(this.props.rawConfig)
} catch (err) {
this.configError = (err as Error).message
}
}
template() {
if (this.configError) {
return <div class="chart-error">Invalid chart config: {this.configError}</div>
}
return <div class="chart">{/* render using this.parsedConfig */}</div>
}
}
This keeps the failure local to Chart and lets its template render a fallback, instead of taking down every sibling in the tree along with it.
Keep created() and onAfterRender() free of anything that can throw for reasons outside your control. Network calls, in particular, don't belong directly in these hooks — kick them off through a Store method that catches its own errors and exposes an error property (see Async Errors and Store-Level Error State), and have the component simply read that reactive state. That moves the actual point of failure out of the synchronous construction path entirely.
Treat any component without an obvious answer to "what happens if this throws?" as a risk. Given that a single failure here can blank out the entire page rather than just the one component, it's worth being conservative about what you allow to run unguarded during construction — especially for components mounted near the root of the tree, where the blast radius of a failure is largest.