Skip to Content
v1.33.8 · shipped One-screen report · Looks like your app Read the changelog →
Migration guidesSPA → SSR

SPA → SSR (Next.js / Nuxt / SvelteKit)

Weeks Med risk

Move a Vite SPA (React, Vue, or Svelte) onto a server-rendered framework (Next.js, Nuxt, or SvelteKit respectively) for SEO, faster TTFB, and real route-level data loading.

The Mushi Mushi bits are: env-var prefix changes, where to mount the provider so it survives navigation, and what to do about hydration warnings. The framework-level migration mechanics are upstream:

Env-var prefix mapping

This is the change that most often breaks the SDK after a move:

Vite SPANext.jsNuxtSvelteKit
VITE_MUSHI_PROJECT_IDNEXT_PUBLIC_MUSHI_PROJECT_IDNUXT_PUBLIC_MUSHI_PROJECT_IDVITE_MUSHI_PROJECT_ID (unchanged)
VITE_MUSHI_API_KEYNEXT_PUBLIC_MUSHI_API_KEYNUXT_PUBLIC_MUSHI_API_KEYVITE_MUSHI_API_KEY (unchanged)
import.meta.env.VITE_*process.env.NEXT_PUBLIC_*useRuntimeConfig().public.* (declare the keys in nuxt.config)import.meta.env.VITE_*

SvelteKit runs on Vite, so VITE_ variables keep working; if you prefer SvelteKit’s own PUBLIC_ prefix, read them from $env/static/public instead. Rerunning npx mushi-mushi init in the new project writes the prefix the CLI uses for that framework into your .env.local.

Migration checklist (generic, picks the right framework as you go)

Migration checklist
12 required steps
  1. Step 01
    Pick the target framework
  2. Step 02
    Bootstrap the new framework alongside the old SPA
  3. Step 03
    Port shared design tokens / global CSS first
  4. Step 04
    Migrate routes one at a time
  5. Step 05
    Move data fetching to the server
  6. Step 06
    Rename env vars per the table above
  7. Step 07
    Mount Mushi Mushi at the root of the new framework
  8. Step 08
    Verify no hydration warnings
  9. Step 09
    For Nuxt/Next, use a .client suffix or "use client" for Mushi Mushi code
  10. Step 10
    Confirm SSR routes are crawlable
  11. Step 11
    Redirect old SPA routes to the new ones (when all migrated)
  12. Step 12
    Smoke-test Mushi Mushi from a server-rendered page

Where the Mushi Mushi widget lives across SSR vs CSR

The Mushi Mushi visual widget mounts client-side, in a Shadow DOM child of document.body, after Mushi.init() runs. Because it doesn’t render in the server-side HTML, you’ll see:

  • ✅ No hydration warnings caused by the widget itself
  • ✅ Crawlers don’t see the widget (it’s not in the initial HTML)
  • ⚠️ The widget appears only after the client-side JavaScript runs, so on slow networks the page shows briefly without it (intentional, to keep server HTML lean)

Common gotchas

  • useMushi() in a Server Component (Next.js App Router): build error. Add 'use client'. See the Next.js Pages → App Router guide for the recommended pattern.
  • Calling Mushi.init() as well as the framework adapter. Since 1.0, MushiPlugin (Vue/Nuxt) and initMushi() (Svelte) initialise the SDK themselves; a second Mushi.init() only logs “Already initialized”. Remove it.
  • Env-var prefix typos. The SDK throws an error saying projectId and apiKey are required. Run npx mushi-mushi init to regenerate the env section.

References

Last updated on