Error Boundaries

An error boundary wraps part of your page. If something inside it throws while mounting, the boundary shows a fallback instead and the rest of the page carries on.

source
<error-boundary fallback="<p>Something went wrong</p>">
  <BuggyComponent />
</error-boundary>

fallback is a literal string of HTML written straight into the boundary. The compiler never walks it, so an expression in it, or a component tag - fallback="<ErrorPage />" - is a build error naming the fallback. Keep it to plain markup.

What it catches

"Caught" means the fallback renders.

Several rows below are still reported to setErrorHandler and the dev overlay even when the fallback does not appear.

Where the error is thrown Fallback renders?
A component <script> body Yes
An onMount() callback Yes
An effect(), on its first run during mount Yes
An effect(), on a later re-run No - caught per effect and reported
A template expression No - reported partially
An event handler No - use try/catch
An async operation No - use try/catch

The first three rows are one rule: anything that throws while a component is initialising rejects that component's mount, and the nearest enclosing boundary takes over.

An effect's later re-runs happen in the scheduler instead, which catches per effect so one bad effect cannot stop the others.

With no boundary above it, the same throw is reported to the error handler and the dev overlay, and the rest of the page still renders.

A boundary changes where an error goes, not whether it is survivable.

The helper reference carries the same table with the reporting column.

Nesting

The innermost boundary wins, so failures stay local:

source
<error-boundary fallback="<p>Outer fallback</p>">
  <error-boundary fallback="<p>Inner fallback</p>">
    <RiskyWidget />
  </error-boundary>
  <SafeWidget />  <!-- Still renders if RiskyWidget fails -->
</error-boundary>

That is the useful shape for a dashboard: one boundary per widget, so a broken chart costs you the chart and nothing else.

source
<div class="dashboard">
  <error-boundary fallback="<p>Chart unavailable</p>">
    <Chart data={chartData} />
  </error-boundary>

  <error-boundary fallback="<p>Stats unavailable</p>">
    <Stats data={statsData} />
  </error-boundary>
</div>

Reporting errors

setErrorHandler() receives every error JAF catches, boundary or not. It and handleError() are the two error functions you have to import:

source
import { setErrorHandler } from 'jafjs'

setErrorHandler((error, info) => {
  console.error('Caught:', error.message)
  console.log('Component:', info.component)

  // Send to error tracking
  analytics.trackError(error, info)
})

The handler is (error, info) => void, where info carries an optional component name.

There is one handler at a time; setting a new one replaces it.

Event handlers and async work are yours to catch. Pass what you catch to handleError() and it joins the same pipeline:

source
<script>
import { handleError } from 'jafjs'

async function loadData() {
  try {
    const res = await fetch('/api/data')
    data = await res.json()
  } catch (error) {
    handleError(error, { component: 'DataLoader' })
  }
}
</script>

In development every error also raises an overlay with the message, the stack and the component name where one is known. Press Esc to dismiss it.