The ClientGate component ensures content is rendered only after JavaScript has loaded client-side, with optional fallback support for server-side rendering or no-JS environments.
When to Use
Render components that depend on browser APIs (window, document, etc.)
Prevent hydration mismatches from client-only features
Show skeleton loaders during hydration
Progressively enhance server-rendered content
Handle components that require JavaScript to function
Basic Usage
Simple Usage
Render Function
import { ClientGate } from "@zayne-labs/ui-react/common/client-gate" ;
function App () {
return (
< ClientGate fallback = { < div > Loading... </ div > } >
< InteractiveChart />
</ ClientGate >
);
}
Component API
children
React.ReactNode | (() => React.ReactNode)
required
Content to render after client-side hydration. Can be a render function for deferred initialization
Content to show during SSR or before hydration. Should match the dimensions of the hydrated content to avoid layout shift
You are encouraged to add a fallback that matches the dimensions of the client-rendered children to avoid content layout shift.
Examples
Browser API-Dependent Component
import { ClientGate } from "@zayne-labs/ui-react/common/client-gate" ;
function GeolocationDisplay () {
return (
< ClientGate fallback = { < div > Detecting location... </ div > } >
{ () => {
const [ location , setLocation ] = useState ( null );
useEffect (() => {
navigator . geolocation . getCurrentPosition (( position ) => {
setLocation ({
lat: position . coords . latitude ,
lng: position . coords . longitude ,
});
});
}, []);
return location ? (
< div >
Lat: { location . lat } , Lng: { location . lng }
</ div >
) : (
< div > Loading location... </ div >
);
} }
</ ClientGate >
);
}
Chart with Skeleton Fallback
import { ClientGate } from "@zayne-labs/ui-react/common/client-gate" ;
function ChartSkeleton () {
return (
< div className = "w-full h-[400px] bg-gray-100 animate-pulse rounded-lg" />
);
}
function AnalyticsDashboard () {
return (
< div >
< h1 > Sales Analytics </ h1 >
< ClientGate fallback = { < ChartSkeleton /> } >
{ () => < RechartsComponent data = { salesData } /> }
</ ClientGate >
</ div >
);
}
import { ClientGate } from "@zayne-labs/ui-react/common/client-gate" ;
function SocialEmbed () {
return (
< ClientGate
fallback = {
< div className = "w-full h-[500px] flex items-center justify-center bg-gray-50" >
< p > Loading embed... </ p >
</ div >
}
>
{ () => (
< div >
< TwitterEmbed tweetId = "123456789" />
</ div >
) }
</ ClientGate >
);
}
Window Size Dependent Component
import { ClientGate } from "@zayne-labs/ui-react/common/client-gate" ;
import { useWindowSize } from "@zayne-labs/toolkit-react" ;
function ResponsiveComponent () {
return (
< ClientGate fallback = { < div className = "h-screen" > Loading... </ div > } >
{ () => {
const { width } = useWindowSize ();
return (
< div >
{ width > 768 ? (
< DesktopLayout />
) : (
< MobileLayout />
) }
</ div >
);
} }
</ ClientGate >
);
}
Comparison to Native Patterns
Traditional Approach
function ChartComponent () {
const [ isClient , setIsClient ] = useState ( false );
useEffect (() => {
setIsClient ( true );
}, []);
if ( ! isClient ) {
return < div > Loading chart... </ div > ;
}
return < Chart /> ;
}
With ClientGate
function ChartComponent () {
return (
< ClientGate fallback = { < div > Loading chart... </ div > } >
< Chart />
</ ClientGate >
);
}
Common Use Cases
Progressive Enhancement
import { ClientGate } from "@zayne-labs/ui-react/common/client-gate" ;
function ArticlePage ({ article }) {
return (
< article >
< h1 > { article . title } </ h1 >
< div > { article . content } </ div >
{ /* Enhanced features only on client */ }
< ClientGate fallback = { null } >
< ShareButtons />
< CommentSection />
< RelatedArticles />
</ ClientGate >
</ article >
);
}
Preventing Hydration Mismatches
import { ClientGate } from "@zayne-labs/ui-react/common/client-gate" ;
function UserGreeting () {
return (
< ClientGate fallback = { < div > Welcome! </ div > } >
{ () => {
const hour = new Date (). getHours ();
const greeting =
hour < 12 ? "Good morning"
: hour < 18 ? "Good afternoon"
: "Good evening" ;
return < div > { greeting } ! </ div > ;
} }
</ ClientGate >
);
}
Avoid using ClientGate for content that should be SEO-friendly. The fallback is what search engines will see.
Implementation Details
The component uses the useIsHydrated hook internally to detect when the client has hydrated:
function ClientGate ( props : ClientGateProps ) {
const { children , fallback } = props ;
const isHydrated = useIsHydrated ();
const resolvedChildren = () => ( isFunction ( children ) ? children () : children );
return isHydrated ? resolvedChildren () : fallback ;
}