SSR framework for Svelte 5 + Bun with islands-based selective hydration
On this page
Request context
Inside components and server-side helpers, import context values directly from mochi-framework:
import { url, params, cookies, locals } from 'mochi-framework';Each export is a proxy that reads from the current request’s AsyncLocalStorage context on every property access — no need to thread values through props.
url
The current page URL as a standard URL object.
<!-- file: src/Post.svelte -->
<script>
import { url } from 'mochi-framework';
const page = Number(url.searchParams.get('page') ?? '1');
</script>
<p>Current path: {url.pathname}</p>url is isomorphic — it works on both server and client:
- Server: proxies
getRequestContext().url, the parsed request URL. - Client: proxies
new URL(window.location.href), constructed fresh on each access so it always reflects the current browser URL (including afterpushState/replaceState).
params
Route parameters matched by the Bun router. Server-only.
<!-- file: src/Post.svelte -->
<script>
import { params } from 'mochi-framework';
</script>
<h1>{params.slug}</h1>cookies
Read and write cookies on both server and client through one API. See the Cookies demo for a full example.
<script>
import { cookies, isBrowser } from 'mochi-framework';
const theme = cookies.get('theme') ?? 'light';
</script>locals
Per-request data set by middleware. Server-only.
// file: src/middleware.ts
import type { Handle } from 'mochi-framework';
export const auth: Handle = async ({ event, resolve }) => {
event.locals.user = await getUser(event.cookies);
return resolve(event);
};<!-- file: src/Dashboard.svelte -->
<script>
import { locals } from 'mochi-framework';
const user = locals.user;
</script>getRequestContext()
Returns the full context object with all fields. Server-only. Prefer the individual exports above unless you need multiple fields at once.
import { getRequestContext } from 'mochi-framework';
const { url, params, cookies, locals, request, requestId } = getRequestContext();Do NOT call getRequestContext() or access params / locals on the client; they throw. Use isServer to guard server-only branches. url and cookies work on both sides.
The context also carries isWarmup — true when the request was issued by route warmup at startup, not a real client. Guard side effects in serverProps that shouldn’t fire for synthetic warmup hits:
serverProps: async () => {
const ctx = getRequestContext();
if (!ctx.isWarmup) await recordVisit(ctx.url.pathname); // skip warmup
return { posts: await loadPosts() };
};