ClientGate

Conditionally render content only after client-side hydration.

The ClientGate component ensures content is rendered only after JavaScript has loaded client-side. This is essential for preventing hydration mismatches and handling browser-only APIs gracefully in SSR environments like Next.js.

Installation

npm install @zayne-labs/ui-react

Preview

Loading...

Basic Usage

Standard usage wraps children that depend on browser APIs.

import { ClientGate } from "@zayne-labs/ui-react/common/client-gate";

function App() {
  return (
    <ClientGate fallback={<div className="h-64 bg-gray-100 animate-pulse" />}>
      <InteractiveMap region="Global" />
    </ClientGate>
  );
}

Use a render callback for deferred initialization of browser-only logic.

import { ClientGate } from "@zayne-labs/ui-react/common/client-gate";

function App() {
  return (
    <ClientGate fallback={<div>Loading Browser Data...</div>}>
      {() => (
        <div>
          Window width: {window.innerWidth}px
        </div>
      )}
    </ClientGate>
  );
}

Component Source

"use client";import { useIsHydrated } from "@zayne-labs/toolkit-react";import { isFunction } from "@zayne-labs/toolkit-type-helpers";export type ClientGateProps = {	/**	 * You are encouraged to add a fallback that is the same dimensions	 * as the client rendered children. This will avoid content layout	 * shift which is disgusting 🥲	 */	children: React.ReactNode | (() => React.ReactNode);	fallback?: React.ReactNode;};/** * @description Render the children only after the JS has loaded client-side. Use an optional * fallback component if the JS is not yet loaded. * * @example * **Render a Chart component if JS loads, renders a simple FakeChart * component server-side or if there is no JS. The FakeChart can have only the * UI without the behavior or be a loading spinner or skeleton.** * * ```tsx * return ( *   <ClientOnly fallback={<FakeChart />}> *     {() => <Chart />} *   </ClientOnly> * ); * ``` */function ClientGate(props: ClientGateProps) {	const { children, fallback } = props;	const isHydrated = useIsHydrated();	const resolvedChildren = () => (isFunction(children) ? children() : children);	return isHydrated ? resolvedChildren() : fallback;}export { ClientGate };

Component API

Why use ClientGate?

  • Zero Hydration Mismatches: Guarantees the server HTML and client initial render are identical (or correctly deferred).
  • Safe Browser Access: Safely access window, localStorage, or document within the children.
  • Improved CLS: By providing a matching fallback, you can maintain Cumulative Layout Shift scores.
// ❌ Dangerous (causes mismatch or crash)
const isDesktop = window.innerWidth > 1024;

// ✅ Safe with ClientGate
<ClientGate fallback={<MobileNav />}>
	<DesktopNav />
</ClientGate>;

Content inside a ClientGate is invisible to search engines and SEO bots because it only appears after hydration. Use it sparingly for non-critical UI elements or interactive widgets.

Edit on GitHub

Last updated on

On this page