useIsClient

PreviousNext

A React hook to check if the component is rendered on the client side, useful for SSR/hydration scenarios.

About

The useIsClient hook is a simple utility that helps you determine if your React component is currently rendering on the client side. This is particularly useful in Next.js applications where components can render both on the server (SSR) and client side.

Features

  • SSR Safe: Prevents hydration mismatches
  • Simple API: Just returns a boolean value
  • Lightweight: Minimal performance impact
  • TypeScript: Fully typed

Use Cases

  • Preventing hydration mismatches with dynamic content
  • Conditionally rendering client-only components
  • Accessing browser APIs safely
  • Progressive enhancement patterns

Preview

Installation

pnpm dlx shadcn@latest add https://ui.nowts.app/r/use-is-client.json

Usage

Basic usage

import { useIsClient } from "@/hooks/use-is-client"
 
export function ClientOnlyComponent() {
  const isClient = useIsClient()
 
  if (!isClient) {
    return <div>Loading...</div>
  }
 
  return (
    <div>
      <p>This content only renders on the client!</p>
      <p>Current URL: {window.location.href}</p>
    </div>
  )
}

Conditional rendering with browser APIs

import { useIsClient } from "@/hooks/use-is-client"
 
export function GeolocationComponent() {
  const isClient = useIsClient()
  const [location, setLocation] = useState<string | null>(null)
 
  useEffect(() => {
    if (isClient && navigator.geolocation) {
      navigator.geolocation.getCurrentPosition(
        (position) => {
          setLocation(
            `${position.coords.latitude}, ${position.coords.longitude}`
          )
        },
        (error) => {
          setLocation("Location unavailable")
        }
      )
    }
  }, [isClient])
 
  if (!isClient) {
    return <div>Checking location availability...</div>
  }
 
  return (
    <div>
      <h3>Your Location</h3>
      <p>{location || "Getting location..."}</p>
    </div>
  )
}

Progressive enhancement

import { useIsClient } from "@/hooks/use-is-client"
 
export function EnhancedButton() {
  const isClient = useIsClient()
  const [interactionCount, setInteractionCount] = useState(0)
 
  const handleClick = () => {
    setInteractionCount((prev) => prev + 1)
 
    // Client-only enhancements
    if (isClient) {
      // Haptic feedback on mobile
      if ("vibrate" in navigator) {
        navigator.vibrate(50)
      }
 
      // Analytics tracking
      gtag?.("event", "button_click", {
        interaction_count: interactionCount + 1,
      })
    }
  }
 
  return (
    <button
      onClick={handleClick}
      className="rounded bg-blue-500 px-4 py-2 text-white"
    >
      Click me {isClient ? `(${interactionCount})` : ""}
    </button>
  )
}

Preventing hydration mismatches

import { useIsClient } from "@/hooks/use-is-client"
 
export function DynamicContent() {
  const isClient = useIsClient()
 
  // This would cause hydration mismatch without useIsClient
  const randomId = Math.random().toString(36).substr(2, 9)
 
  return (
    <div>
      <h2>Dynamic Content</h2>
      {isClient ? <p>Random ID: {randomId}</p> : <p>Random ID: Loading...</p>}
    </div>
  )
}

Working with local storage

import { useIsClient } from "@/hooks/use-is-client"
 
export function UserPreferences() {
  const isClient = useIsClient()
  const [theme, setTheme] = useState<string>("light")
 
  useEffect(() => {
    if (isClient) {
      const savedTheme = localStorage.getItem("theme") || "light"
      setTheme(savedTheme)
    }
  }, [isClient])
 
  const toggleTheme = () => {
    const newTheme = theme === "light" ? "dark" : "light"
    setTheme(newTheme)
 
    if (isClient) {
      localStorage.setItem("theme", newTheme)
    }
  }
 
  if (!isClient) {
    return <div>Loading preferences...</div>
  }
 
  return (
    <div
      className={`p-4 ${theme === "dark" ? "bg-gray-800 text-white" : "bg-white text-black"}`}
    >
      <h3>Theme: {theme}</h3>
      <button onClick={toggleTheme}>
        Switch to {theme === "light" ? "dark" : "light"} theme
      </button>
    </div>
  )
}