.client モジュール

概要

ブラウザでモジュールの副作用を使用するファイルや依存関係がある場合があります。ファイル名に *.client.ts を使用するか、ファイルを .client ディレクトリ内にネストすることで、それらをサーバーバンドルから強制的に除外できます。

feature-check.client.ts
// this would break the server
export const supportsVibrationAPI =
  "vibrate" in window.navigator;

このモジュールからエクスポートされる値はすべてサーバー上では undefined になるため、それらを使用できるのは useEffect やクリックハンドラーのようなユーザーイベントのみであることに注意してください。

import { supportsVibrationAPI } from "./feature-check.client.ts";
 
console.log(supportsVibrationAPI);
// server: undefined
// client: true | false

クライアント/サーバーバンドルに含まれるものをより高度に制御する必要がある場合は、vite-env-only プラグインを確認してください。

使用パターン

個々のファイル

ファイル名に .client を追加することで、個々のファイルをクライアント専用としてマークします。

app/
├── utils.client.ts        👈 クライアント専用ファイル
├── feature-detection.client.ts
└── root.tsx

クライアントディレクトリ

ディレクトリ名に .client を使用することで、ディレクトリ全体をクライアント専用としてマークします。

app/
├── .client/               👈 ディレクトリ全体がクライアント専用
│   ├── analytics.ts
│   ├── feature-detection.ts
│   └── browser-utils.ts
├── components/
└── root.tsx

ブラウザの機能検出

app/utils/browser.client.ts
export const canUseDOM = typeof window !== "undefined";
 
export const hasWebGL = !!window.WebGLRenderingContext;
 
export const supportsVibrationAPI =
  "vibrate" in window.navigator;

クライアント専用ライブラリ

app/analytics.client.ts
// これはサーバー上で動作しません
import { track } from "some-browser-only-analytics-lib";
 
export function trackEvent(eventName: string, data: any) {
  track(eventName, data);
}

クライアントモジュールの使用

app/routes/dashboard.tsx
import { useEffect } from "react";
import {
  canUseDOM,
  supportsLocalStorage,
  supportsVibrationAPI,
} from "../utils/browser.client.ts";
import { trackEvent } from "../analytics.client.ts";
 
export default function Dashboard() {
  useEffect(() => {
    // これらの値はサーバー上では undefined です
    if (canUseDOM && supportsVibrationAPI) {
      console.log("Device supports vibration");
    }
 
    // 安全な localStorage の使用
    const savedTheme =
      supportsLocalStorage.getItem("theme");
    if (savedTheme) {
      document.body.className = savedTheme;
    }
 
    trackEvent("dashboard_viewed", {
      timestamp: Date.now(),
    });
  }, []);
 
  return <div>Dashboard</div>;
}