svelte/reactivity/window - リアクティブなウィンドウ値

svelte/reactivity/window モジュールは、window オブジェクトの各種プロパティをリアクティブな値として提供します。 <svelte:window> バインディングや手動のイベントリスナーを使用せずに、ウィンドウの状態を監視できます。

この記事で学べること

  • 利用可能なリアクティブ値
  • 各値の使い方と特徴
  • 実践的なユースケース
  • SSR での動作
バージョン要件

このモジュールは Svelte 5.11.0 以降で利用可能です。

基本的な使い方

このモジュールからエクスポートされる値は、すべて .current プロパティを持つオブジェクトです。このプロパティは自動的にウィンドウの状態と同期され、値が変化すると依存するUIも自動的に更新されます。

<script lang="ts">
  import { innerWidth, innerHeight } from 'svelte/reactivity/window';
</script>

<p>ウィンドウサイズ: {innerWidth.current}x{innerHeight.current}</p>
svelte

利用可能な値

以下のリアクティブ値がモジュールから利用可能です。各値はウィンドウの対応するプロパティと同期しており、プロパティの変化を自動的に検知します。

ウィンドウサイズ

ウィンドウのサイズを取得するための値です。innerWidth/innerHeight はスクロールバーを除いた表示領域、outerWidth/outerHeight はブラウザウィンドウ全体のサイズを表します。

説明
innerWidthウィンドウの内側の幅(px)
innerHeightウィンドウの内側の高さ(px)
outerWidthウィンドウの外側の幅(px)
outerHeightウィンドウの外側の高さ(px)
<script lang="ts">
  import {
    innerWidth,
    innerHeight,
    outerWidth,
    outerHeight
  } from 'svelte/reactivity/window';
</script>

<div class="size-display">
  <p>内側サイズ: {innerWidth.current}x{innerHeight.current}</p>
  <p>外側サイズ: {outerWidth.current}x{outerHeight.current}</p>
</div>
svelte

スクロール位置

ページのスクロール位置をリアクティブに取得できます。スクロールに応じたUIの表示/非表示や、プログレスバーの実装などに便利です。

説明
scrollX水平スクロール位置(px)
scrollY垂直スクロール位置(px)

以下の例では、スクロール位置を表示し、一定以上スクロールした場合に「トップへ戻る」ボタンを表示しています。

<script lang="ts">
  import { scrollX, scrollY } from 'svelte/reactivity/window';

  // スクロール位置に基づく派生値
  let showBackToTop = $derived((scrollY.current ?? 0) > 300);
</script>

<div class="scroll-indicator">
  スクロール: ({scrollX.current}, {scrollY.current})
</div>

{#if showBackToTop}
  <button
    class="back-to-top"
    onclick={() => window.scrollTo({ top: 0, behavior: 'smooth' })}
  >
    トップへ戻る
  </button>
{/if}
svelte

画面位置

ブラウザウィンドウのスクリーン上での位置を取得できます。マルチモニター環境でのウィンドウ配置の検知などに使用できます。

説明
screenLeftウィンドウの左端のスクリーン座標
screenTopウィンドウの上端のスクリーン座標
<script lang="ts">
  import { screenLeft, screenTop } from 'svelte/reactivity/window';
</script>

<p>ウィンドウ位置: ({screenLeft.current}, {screenTop.current})</p>
svelte
更新タイミング

screenLeftscreenToprequestAnimationFrame コールバック内で更新されます。

デバイスピクセル比

デバイスピクセル比(DPR)は、CSSピクセルと物理ピクセルの比率を表します。Retina ディスプレイでは 2 以上の値になります。高解像度画像の出し分けなどに使用できます。

<script lang="ts">
  import { devicePixelRatio } from 'svelte/reactivity/window';
</script>

<p>デバイスピクセル比: {devicePixelRatio.current}</p>
svelte
ブラウザごとの違い

devicePixelRatio の動作はブラウザによって異なります。

  • Chrome: ズームレベルに応じて変化
  • Firefox/Safari: ズームレベルに反応しない

オンライン状態

ブラウザがインターネットに接続しているかどうかを検知できます。オフライン時の警告表示や、オンライン復帰時のデータ同期などに活用できます。

<script lang="ts">
  import { online } from 'svelte/reactivity/window';
</script>

{#if online.current}
  <span class="status online">オンライン</span>
{:else}
  <span class="status offline">オフライン</span>
{/if}
svelte

実践例

これらのリアクティブ値を組み合わせることで、様々なUIパターンを実装できます。以下に実践的なユースケースを紹介します。

レスポンシブレイアウト

ウィンドウ幅に基づいてデバイスタイプを判定し、レイアウトを動的に切り替える例です。CSS メディアクエリでは対応しにくいJavaScriptベースの条件分岐に便利です。

<script lang="ts">
  import { innerWidth } from 'svelte/reactivity/window';

  // ブレークポイントの定義
  const breakpoints = {
    mobile: 640,
    tablet: 1024,
    desktop: 1280
  };

  let deviceType = $derived(() => {
    const width = innerWidth.current ?? 0;
    if (width < breakpoints.mobile) return 'mobile';
    if (width < breakpoints.tablet) return 'tablet';
    if (width < breakpoints.desktop) return 'laptop';
    return 'desktop';
  });

  let isMobile = $derived((innerWidth.current ?? 0) < breakpoints.mobile);
</script>

<div class="layout" class:mobile={isMobile}>
  <header>
    {#if isMobile}
      <button class="menu-toggle">メニュー</button>
    {:else}
      <nav>
        <a href="/">ホーム</a>
        <a href="/about">概要</a>
        <a href="/contact">連絡先</a>
      </nav>
    {/if}
  </header>

  <main>
    <p>現在のデバイス: {deviceType}</p>
  </main>
</div>
svelte

スクロールに応じたヘッダー

スクロール方向を検知して、下方向にスクロール中はヘッダーを非表示にし、上方向にスクロール中は表示する「スマートヘッダー」パターンの実装例です。

<script lang="ts">
  import { scrollY } from 'svelte/reactivity/window';

  // 前回のスクロール位置を追跡
  let prevScrollY = $state(0);

  // スクロール方向を検出
  let isScrollingDown = $derived(() => {
    const current = scrollY.current ?? 0;
    const down = current > prevScrollY;
    // 次回の比較のために保存
    prevScrollY = current;
    return down;
  });

  // ヘッダーの表示/非表示
  let showHeader = $derived(() => {
    const y = scrollY.current ?? 0;
    // 上部近くでは常に表示
    if (y < 100) return true;
    // 上にスクロール中は表示
    return !isScrollingDown;
  });
</script>

<header class:hidden={!showHeader}>
  <nav>ナビゲーション</nav>
</header>

<style>
  header {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    background: white;
    transition: transform 0.3s;
  }

  header.hidden {
    transform: translateY(-100%);
  }
</style>
svelte

オンライン/オフライン通知

ネットワーク接続状態の変化をユーザーに通知する例です。オフラインになった時に警告を表示し、オンラインに復帰した時には一時的な成功メッセージを表示します。

<script lang="ts">
  import { online } from 'svelte/reactivity/window';
  import { fly } from 'svelte/transition';

  let wasOffline = $state(false);

  // オンラインに戻った時に通知
  let showReconnected = $derived(() => {
    if (!online.current) {
      wasOffline = true;
      return false;
    }
    if (wasOffline) {
      // 3秒後に非表示
      setTimeout(() => wasOffline = false, 3000);
      return true;
    }
    return false;
  });
</script>

{#if !online.current}
  <div class="notification offline" transition:fly={{ y: -50 }}>
    インターネット接続がありません
  </div>
{/if}

{#if showReconnected}
  <div class="notification online" transition:fly={{ y: -50 }}>
    接続が回復しました
  </div>
{/if}

<style>
  .notification {
    position: fixed;
    top: 20px;
    left: 50%;
    transform: translateX(-50%);
    padding: 1rem 2rem;
    border-radius: 8px;
    z-index: 1000;
  }

  .offline {
    background: #dc3545;
    color: white;
  }

  .online {
    background: #28a745;
    color: white;
  }
</style>
svelte

Retina 対応の画像

デバイスピクセル比に応じて適切な解像度の画像を選択する例です。Retina ディスプレイでは高解像度版(@2x、@3x)を、通常ディスプレイでは標準版を読み込みます。

<script lang="ts">
  import { devicePixelRatio } from 'svelte/reactivity/window';

  let { src, alt }: { src: string; alt: string } = $props();

  // デバイスピクセル比に応じた画像サイズを計算
  let imageSuffix = $derived(() => {
    const dpr = devicePixelRatio.current ?? 1;
    if (dpr >= 3) return '@3x';
    if (dpr >= 2) return '@2x';
    return '';
  });

  let imageSrc = $derived(() => {
    const [name, ext] = src.split('.');
    return `${name}${imageSuffix}.${ext}`;
  });
</script>

<img src={imageSrc} {alt} />
svelte

スクロールプログレスバー

ページの読み進め具合を視覚的に表示するプログレスバーの実装例です。スクロール位置とドキュメントの高さから進捗率を計算し、バーの幅を動的に変更しています。

<script lang="ts">
  import { scrollY, innerHeight } from 'svelte/reactivity/window';

  let documentHeight = $state(0);

  // ドキュメントの高さを取得
  $effect(() => {
    documentHeight = document.documentElement.scrollHeight;
  });

  // スクロール進捗(0-100)
  let scrollProgress = $derived(() => {
    const y = scrollY.current ?? 0;
    const height = innerHeight.current ?? 0;
    const maxScroll = documentHeight - height;
    if (maxScroll <= 0) return 0;
    return Math.min(100, (y / maxScroll) * 100);
  });
</script>

<div
  class="progress-bar"
  style="width: {scrollProgress}%"
></div>

<style>
  .progress-bar {
    position: fixed;
    top: 0;
    left: 0;
    height: 3px;
    background: #ff3e00;
    z-index: 1000;
    transition: width 0.1s;
  }
</style>
svelte

SSR での動作

SvelteKit などでサーバーサイドレンダリング(SSR)を使用する場合、window オブジェクトは存在しません。そのため、すべてのリアクティブ値は undefined を返します。Nullish coalescing 演算子(??)を使用してフォールバック値を設定するか、browser チェックを使用してクライアントサイドのみで値を使用するようにしましょう。

<script lang="ts">
  import { innerWidth } from 'svelte/reactivity/window';
  import { browser } from '$app/environment';

  // サーバーサイドでのフォールバック
  let width = $derived(innerWidth.current ?? 1024);

  // または browser チェックを使用
  let isMobile = $derived(() => {
    if (!browser) return false;
    return (innerWidth.current ?? 0) < 640;
  });
</script>

{#if browser}
  <p>ウィンドウ幅: {innerWidth.current}px</p>
{:else}
  <p>クライアントサイドでレンダリングされます</p>
{/if}
svelte

svelte:window との比較

svelte/reactivity/window モジュールは、従来の <svelte:window> バインディングの代替となります。どちらも同じ結果を得られますが、記述量と使い勝手に違いがあります。

従来のアプローチ

<script lang="ts">
  let innerWidth = $state(0);
  let scrollY = $state(0);
</script>

<svelte:window bind:innerWidth bind:scrollY />

<p>幅: {innerWidth}, スクロール: {scrollY}</p>
svelte

svelte/reactivity/window を使用

<script lang="ts">
  import { innerWidth, scrollY } from 'svelte/reactivity/window';
</script>

<p>幅: {innerWidth.current}, スクロール: {scrollY.current}</p>
svelte

比較

項目<svelte:window>svelte/reactivity/window
コード量やや多い簡潔
複数コンポーネント各コンポーネントで定義が必要インポートするだけ
型安全性手動で型定義型が事前定義済み
SSR 対応手動でハンドリングundefined を返す

型定義

TypeScript を使用する場合、すべての値は以下の型定義に従います。current プロパティは undefined を含む可能性があるため、適切な型ガードやフォールバック値を使用してください。

interface ReactiveValue<T> {
  readonly current: T;
}

// 各値の型
const innerWidth: ReactiveValue<number | undefined>;
const innerHeight: ReactiveValue<number | undefined>;
const outerWidth: ReactiveValue<number | undefined>;
const outerHeight: ReactiveValue<number | undefined>;
const scrollX: ReactiveValue<number | undefined>;
const scrollY: ReactiveValue<number | undefined>;
const screenLeft: ReactiveValue<number | undefined>;
const screenTop: ReactiveValue<number | undefined>;
const devicePixelRatio: { readonly current: number | undefined };
const online: ReactiveValue<boolean | undefined>;
typescript

まとめ

svelte/reactivity/window を使用することで、以下のことが実現できます。

  • 簡潔なコード: <svelte:window> バインディングより簡潔
  • 再利用性: 複数のコンポーネントで同じ値を使用
  • 型安全性: TypeScript の型が事前定義済み
  • SSR 対応: サーバーサイドでは undefined を返す

ウィンドウの状態に基づいたリアクティブな UI を構築する際に非常に便利なモジュールです。

次のステップ

Last update at: 2026/01/11 15:56:32