.client
modulesYou may have a file or dependency that uses module side effects in the browser. You can use *.client.ts
on file names or nest files within .client
directories to force them out of server bundles.
// this would break the server
export const supportsVibrationAPI =
"vibrate" in window.navigator;
Note that values exported from this module will all be undefined
on the server, so the only places to use them are in useEffect
and user events like click handlers.
import { supportsVibrationAPI } from "./feature-check.client.ts";
console.log(supportsVibrationAPI);
// server: undefined
// client: true | false
Mark individual files as client-only by adding .client
to the filename:
app/
├── utils.client.ts 👈 client-only file
├── feature-detection.client.ts
└── root.tsx
Mark entire directories as client-only by using .client
in the directory name:
app/
├── .client/ 👈 entire directory is client-only
│ ├── analytics.ts
│ ├── feature-detection.ts
│ └── browser-utils.ts
├── components/
└── root.tsx
export const canUseDOM = typeof window !== "undefined";
export const hasWebGL = !!window.WebGLRenderingContext;
export const supportsVibrationAPI =
"vibrate" in window.navigator;
// This would break on the server
import { track } from "some-browser-only-analytics-lib";
export function trackEvent(eventName: string, data: any) {
track(eventName, data);
}
import { useEffect } from "react";
import {
canUseDOM,
supportsLocalStorage,
supportsVibrationAPI,
} from "../utils/browser.client.ts";
import { trackEvent } from "../analytics.client.ts";
export default function Dashboard() {
useEffect(() => {
// These values are undefined on the server
if (canUseDOM && supportsVibrationAPI) {
console.log("Device supports vibration");
}
// Safe localStorage usage
const savedTheme =
supportsLocalStorage.getItem("theme");
if (savedTheme) {
document.body.className = savedTheme;
}
trackEvent("dashboard_viewed", {
timestamp: Date.now(),
});
}, []);
return <div>Dashboard</div>;
}