## Demo: error-boundaries ### ErrorBoundaries.svelte ```svelte {#snippet caughtFallback(error: unknown)}
Caught by user-written boundary: {error instanceof Error ? error.message : String(error)}
{/snippet}

1. Non-hydrated component + user <svelte:boundary>

Non-hydrated components don't get an automatic boundary. If they can throw during SSR, wrap them in a hand-written <svelte:boundary> — otherwise the throw bubbles up and crashes the whole request.

2. mochi:hydrate — SSR throw (recovery)

The framework auto-wraps every mochi:hydrate island in a boundary. This island throws on the server only — the boundary catches the SSR throw and the rest of the page renders unaffected.

3. mochi:hydrate — client throw

SSR is fine, but the script throws synchronously once the island tries to hydrate. The defensive try/catch around hydrate() catches it and swaps the island for the failure stub — the rest of the page (already rendered on the server) is untouched.

4. mochi:defer — server island throw

Server islands render at a separate endpoint. When that render throws, the endpoint returns a 200 with an error stub — the browser doesn't waste retries on a deterministic failure. The user-supplied loading children stay until the response arrives.

Loading from server…

5. mochi:defer — healthy island, inner mochi:hydrate client throw

The server island itself renders successfully — it's the inner mochi:hydrate child that throws once it tries to hydrate on the client. The child's auto-boundary catches its own failure, so the rest of the server island content stays intact.

Loading from server…
``` ### ThrowOnSsr.svelte ```svelte
On the server this island threw, so the boundary swapped in the failure stub — you briefly see it flash before hydration takes over, or if you disable JavaScript. Then the client re-renders the component cleanly and replaces the stub with this content. This works but is not recommended.
``` ### ThrowOnClient.svelte ```svelte
This island renders fine on the server. Once the client tries to hydrate, the script throws synchronously — the boundary catches it and swaps in the failure stub.
``` ### ThrowOnServerIsland.svelte ```svelte
never rendered
``` ### HealthyServerIsland.svelte ```svelte

The server island itself rendered successfully.

Inside it sits a mochi:hydrate child that throws on the client — its boundary catches the failure independently:

``` ### routes.ts ```ts import { Mochi } from 'mochi-framework'; import type { MochiRouteValue } from 'mochi-framework'; export const routes: Record = { '/demos/error-boundaries': Mochi.page('./src/demos/error-boundaries/ErrorBoundaries.svelte'), }; ``` ### index.ts ```ts import { Mochi, logger } from 'mochi-framework'; await Mochi.serve({ port: 3333, development: process.env.MODE === 'development', routes: { '/': Mochi.page('./src/Home.svelte'), }, }); logger.info('Server running at http://localhost:3333'); ```