Svelte 5のコンテキストAPI(setContext/getContext)を整理した話
Svelte 5のコンテキストAPI(setContext / getContext)を整理します。propsとstoreに続く3つ目の状態共有の方法で、コンポーネントツリー内だけで状態を共有したいときに使います。
3つの状態共有方法の整理
props → 親から子へ直接渡す(一方向)
store → どこからでもimportして使う(グローバル)
context → コンポーネントツリー内だけで共有する(スコープ付き)
contextはstoreとpropsの中間的な存在です。
基本的な使い方
<!-- 親コンポーネント -->
<script lang="ts">
import { setContext } from 'svelte';
setContext('theme', 'dark');
</script>
<!-- 子・孫コンポーネント(何階層でも) -->
<script lang="ts">
import { getContext } from 'svelte';
const theme = getContext<string>('theme');
</script>
<p>テーマ: {theme}</p>
setContext で登録した値は、そのコンポーネントの子孫からのみ参照できます。親や兄弟コンポーネントからは参照できません。
storeとの違い
store → アプリ全体で1つの値を共有する
context → コンポーネントツリーごとに独立した値を持てる
リスト内のカードコンポーネントで、カードごとに異なる設定を子孫に渡したいときはcontextが向いています。
<CardA> ← setContext('color', 'blue')
<CardHeader /> ← getContext で 'blue' を取得
<CardBody /> ← getContext で 'blue' を取得
<CardB> ← setContext('color', 'red')
<CardHeader /> ← getContext で 'red' を取得
<CardBody /> ← getContext で 'red' を取得
storeだと全カードが同じ値を参照してしまいますが、contextなら各カードが独立した値を持てます。
リアクティブなcontextの作り方
❌ 直接渡すと初期値のコピーになる
<script lang="ts">
import { setContext } from 'svelte';
let { color }: Props = $props();
// ❌ color の初期値がコピーされるだけ
// color が後から変わっても context は更新されない
setContext('cardColor', color);
</script>
このように書くと Svelte から以下のエラーが出ます。
This reference only captures the initial value of `color`.
Did you mean to reference it inside a closure instead?
https://svelte.dev/e/state_referenced_locally
✅ getterで包むと常に最新の値を参照できる
<script lang="ts">
import { setContext } from 'svelte';
let { color }: Props = $props();
// ✅ getter にすると常に最新の color を参照する
setContext('cardColor', {
get color() { return color; }
});
</script>
これはstoreの get count() { return count; } と全く同じパターンです。Svelte 5ではリアクティブな値を外部に渡すときは常にgetterで包むというルールが一貫して適用されます。
実装例:カードコンポーネント
<!-- src/lib/components/Card.svelte -->
<script lang="ts">
import { setContext } from 'svelte';
import type { Snippet } from 'svelte';
type Props = {
color: string;
children: Snippet;
};
let { color, children }: Props = $props();
setContext('cardColor', {
get color() { return color; }
});
</script>
<div style="border: 2px solid {color}; padding: 16px; margin: 8px; border-radius: 8px;">
{@render children()}
</div>
<!-- src/lib/components/CardHeader.svelte -->
<script lang="ts">
import { getContext } from 'svelte';
type Props = { title: string };
let { title }: Props = $props();
const card = getContext<{ color: string }>('cardColor');
</script>
<h2 style="color: {card.color}; margin: 0 0 8px 0;">{title}</h2>
<!-- 使う側 -->
<Card color="blue">
<CardHeader title="ブルーカード" />
<p>このカードのテーマカラーはblueです</p>
</Card>
<Card color="red">
<CardHeader title="レッドカード" />
<p>このカードのテーマカラーはredです</p>
</Card>
使い分けのまとめ
| 方法 | スコープ | 向いているケース |
|---|---|---|
| props | 親→子(直接) | シンプルな親子関係・汎用UIパーツ |
| store | アプリ全体 | ログイン情報・カートなどグローバルな状態 |
| context | コンポーネントツリー内 | 複数インスタンスで独立した状態が必要なとき |
まとめ
| 内容 | |
|---|---|
setContext(key, value) |
子孫コンポーネントに値を渡す |
getContext<型>(key) |
親コンポーネントが設定した値を受け取る |
| getterパターン | リアクティブな値を渡すときはgetterで包む |
state_referenced_locally |
直接渡すと初期値のコピーになるというエラー |