Skip to content

Svelteで使う ​

@tuvix.js/svelteはSvelteコンポーネントをマイクロアプリに変換します。Svelte 5とSvelte 3/4をサポートし、サーバーのマークアップをハイドレートし、サーバーでコンポーネントをHTMLにレンダリングします。

インストール ​

bash
pnpm add @tuvix.js/svelte svelte

コンポーネントをマイクロアプリにする ​

ts
// src/main.ts
import { SvelteMap } from 'svelte/reactivity';
import { createSvelteMicroApp } from '@tuvix.js/svelte';
import Profile from './Profile.svelte';

// SvelteMap (Svelte 5 only) lets update() re-render in place; drop it for Svelte 3/4.
export default createSvelteMicroApp({ name: 'profile', App: Profile, SvelteMap });

最初のマウント時に、アダプターはsvelteをインポートし、どのAPIを持っているかを確認します。

Svelteマウントハイドレート更新アンマウント
5mount(App, { target, props })hydrate(...)SvelteMapがあればその場で更新、なければ再マウントunmount(instance)
3 / 4new App({ target, props })new App({ hydrate: true })instance.$set(props)instance.$destroy()

バンドラーはsvelteをブラウザー向けビルドに解決する必要があります。サーバー向けビルドが選ばれると、Svelte 5のmount()がlifecycle_function_unavailableを投げるか、onMountのコールバックが実行されません。トラブルシューティングを参照してください。

コンテナごとに専用のコンポーネントインスタンスが作成されます。mountは、onMountのコールバックとエフェクトが実行された後にtuvix:mountedをディスパッチします。ssrを指定しない場合、mountはレンダリングの前にコンテナを空にします。このモジュールがマウントしたことのないコンテナ、またはマウントに失敗したコンテナに対するunmountは何もしません。

プロパティ ​

runesを使うSvelte 5の場合:

svelte
<!-- Profile.svelte -->
<script lang="ts">
  let { userId, theme = 'light' }: { userId: string; theme?: string } = $props();
</script>

<div class="theme-{theme}">User {userId}</div>

Svelte 3/4、またはレガシーモードのSvelte 5の場合:

svelte
<script lang="ts">
  export let userId: string;
  export let theme = 'light';
</script>

<div class="theme-{theme}">User {userId}</div>

Svelte 5では、設定にsvelte/reactivityのSvelteMapを渡します。するとアダプターはSvelteMapを裏に持つpropsオブジェクトをコンポーネントに渡し、update({ props })は新しい値をそこに書き込みます。変更されたプロパティを読み取る部分だけが再レンダリングされ、DOMノードとコンポーネントの状態は維持されます。これは$props()でもexport letでも動作します。SvelteMapがない場合、Svelte 5のupdate()はインスタンスをアンマウントし、新しいプロパティで新しいインスタンスをマウントして再びtuvix:mountedをディスパッチするため、ローカルの状態はリセットされます。svelte/reactivityモジュールはSvelte 3/4には存在せず、それらのビルドが失敗するため、アダプター自身はこれをインポートしません。

Svelte 3/4では、updateは$setを呼び出し、SvelteMapを無視します。

各更新はプロパティのセット全体を置き換えます。新しいプロパティにないキーは、Svelte 5ではプロパティのデフォルト値に戻り、Svelte 3/4ではundefinedになります。オーケストレーターは常にマージ後のプロパティを送るため、これが問題になるのは自分でupdateを呼び出す場合だけです。

ts
import { createOrchestrator } from '@tuvix.js/core';

const orchestrator = createOrchestrator();
await orchestrator.updateAppProps('profile', { theme: 'dark' });

サーバーレンダリングとハイドレーション ​

ts
// server-only module
import { render } from 'svelte/server';
import { renderSvelteToString } from '@tuvix.js/svelte/server';
import Footer from './Footer.svelte';

export async function renderFooter(): Promise<string> {
  return renderSvelteToString(Footer, { year: 2026 }, { render });
}
ts
// client
import { createSsrSvelteMicroApp } from '@tuvix.js/svelte';
import Footer from './Footer.svelte';

export const footer = createSsrSvelteMicroApp({ name: 'footer', App: Footer });
  • renderSvelteToString(App, props?, { render? })は、コンポーネントの<style>タグに続けてマークアップを返します。サーバーのコードから呼び出します。ブラウザーでは''を返します。
  • サーバーバンドルにSvelteが含まれる場合(Nitro、Vinxi、noExternalのVite SSRビルド)は、svelte/serverのrenderを渡します。渡さないと、アダプターは実行時にsvelte/serverをインポートします。これにより2つ目のSvelteのコピーが読み込まれることがあり、Svelteのサーバーレンダラーはコピーをまたぐと失敗します。
  • <svelte:head>の出力が必要な場合、renderSvelte(App, props?, { render? })は{ html, head, css }を返します。headは自分でドキュメントの<head>に配置します。アイランドに<title>や<meta>を含めてはいけません。
  • createSsrSvelteMicroAppは、コンテナが要素または空白以外のテキストを持つ場合にハイドレートし、それ以外では新規にレンダリングします。Svelte 3/4のコンポーネントはhydratable: trueでコンパイルする必要があります。

初回描画時のCSS ​

次のいずれかを選びます。

  • HTML内のCSS。 compilerOptions: { css: 'injected' }でコンパイルします。するとSvelte 5はコンポーネントのCSSをサーバーの出力に含め、renderSvelteToStringはそれをアイランド内の<style>タグとして含めます。
  • ファイルとしてのCSS。 デフォルトのemitCss: trueのままにし、@tuvix.js/viteでアイランドのスタイルシートを<head>にリンクします。SSRアイランドを参照してください。

css: 'injected'ではスタイルシートのファイルが存在しないため、@tuvix.js/viteはリンクするものを見つけられません。2つを組み合わせないでください。

イベントバス ​

svelte
<script lang="ts">
  import { onDestroy } from 'svelte';
  import { getGlobalBus } from '@tuvix.js/event-bus';

  let count = $state(0);
  const off = getGlobalBus().on<{ itemCount: number }>('cart:updated', ({ itemCount }) => {
    count = itemCount;
  });
  onDestroy(off);
</script>

<span class="badge">{count}</span>

アダプターが行わないこと ​

  • .svelteファイルはコンパイルしません。それは@sveltejs/vite-plugin-svelteが行います。
  • 渡すのはプロパティだけです。スロット、スニペット、コンポーネントイベントはホストから渡されません。
  • バンドラーがどのSvelteビルドを選んだかは確認しません。

API ​

エクスポート用途
createSvelteMicroApp({ name, App, bootstrap?, ssr?, SvelteMap? })Svelteマイクロアプリのモジュール
createSsrSvelteMicroApp({ name, App, bootstrap?, SvelteMap? })ssr: trueを設定した同等のもの
@tuvix.js/svelte/serverのrenderSvelteToString(App, props?, options?)スタイル + マークアップ
@tuvix.js/svelte/serverのrenderSvelte(App, props?, options?){ html, head, css }