SSR framework for Svelte 5 + Bun with islands-based selective hydration
Server islands with mochi:defer
Server islands allow you to defer rendering of dynamic or personalized components until after the initial page load. The page renders immediately with fallback content, then each server island fetches its own HTML from a dedicated endpoint.
This is useful for personalized content (user avatars, cart counts) that shouldn’t block the rest of the page from being aggressively cached.
<!-- Basic server island — renders on-demand after page load -->
<UserAvatar mochi:defer userId={123} />
<!-- With fallback content shown while loading -->
<UserAvatar mochi:defer userId={123}>
<div class="skeleton">Loading...</div>
</UserAvatar>Server island components are normal Svelte components that run on the server. They have full access to the request context (cookies, headers) via getRequestContext():
<script>
import { getRequestContext } from 'mochi-framework';
const { cookies } = getRequestContext();
const session = cookies.get('session');
// Fetch personalized data...
</script>
<p>Welcome back, {userName}!</p>How it works
- During initial SSR, the component is not rendered — only the fallback content is emitted inside a
<mochi-server-island>custom element - Props are HMAC-signed and passed as a query parameter to prevent tampering
- The browser fetches the component’s HTML from
/_mochi/island/{ComponentName}?p={signedProps}(the/_mochiprefix is configurable viaassetPrefix) - Cookies are forwarded automatically (same-origin fetch), so the component can access the user’s session
- The returned HTML replaces the fallback content
Combining with hydration
Server islands can also be hydrated for client-side interactivity:
<!-- Fetched on-demand, then hydrated for interactivity -->
<ShoppingCart mochi:defer mochi:hydrate items={initialItems} />Props
Props are serialized with devalue — see Passing props to islands for the full list of supported types. Server islands additionally HMAC-sign the payload and pass it as a query parameter; if the signed props exceed URL length limits (~1800 bytes), a warning is emitted.
Prop deduplication
During SSR each island registers its serialized props in a per-request map keyed by the exact devalue output, then carries a small props-ref="mochi-props-<n>" pointer to the matching <script type="application/json"> block hoisted at the top of the body:
<script type="application/json" id="mochi-props-0">
[{ "items": 1 }, [1, 2, 3]]
</script>
…
<mochi-hydratable-island props-ref="mochi-props-0">…</mochi-hydratable-island>
<mochi-hydratable-island props-ref="mochi-props-0">…</mochi-hydratable-island>Two islands whose props serialize byte-identically share a single block — this reduces bytes over the wire when two islands share the same props. Server islands (mochi:defer) are unaffected: their signed-props are HMAC-signed and stay inline so the signature ties to the exact attribute value the client sees.
Encryption key
Props are HMAC-signed with a random key generated at server startup. For multi-instance deployments (rolling deploys, CDN caching), set the MOCHI_KEY environment variable to a constant base64url-encoded 32-byte key so all instances share the same signing key.