🍡 mochi

SSR framework for Svelte 5 + Bun with islands-based selective hydration

Environment constants

Import build-time constants from the virtual mochi-framework module:

import { isServer, isBrowser, isDev } from 'mochi-framework';
ConstantServerBrowserDescription
isServertruefalseTrue during server-side rendering
isBrowserfalsetrueTrue in the browser
isDevvariesvariesTrue when development: true in config

Auto-injected props

The preprocessor injects two props on every component used with mochi:hydrate, mochi:hydrate:visible, or mochi:defer mochi:hydrate:

PropTypeDescription
islandIdstringStable id matching the wrapping <mochi-hydratable-island island-id> attribute.
isHydratabletrue \| undefinedtrue for hydratable invocations; absent on plain SSR-only invocations.

Use isHydratable to branch SSR-only behavior off the same call site that hydrates client-side — e.g. peek the request’s form snapshot only when the client won’t take over:

<script>
  import { isServer, getRequestContext } from 'mochi-framework';

  let { isHydratable } = $props<{ isHydratable?: boolean }>();

  // Hydrated: the client will fill state via the enhance attachment. Plain SSR: read
  // the post-submit form snapshot so the rendered HTML reflects the last
  // action result.
  const initial = isHydratable || !isServer ? null : peekForm();

  function peekForm() {
    const f = getRequestContext().form;
    return f && f.ok && typeof f.data.value === 'number' ? f.data.value : null;
  }
</script>

See the Forms demo for a side-by-side comparison of hydrated and SSR-only render paths.